我正在创建一个应该使用字段的元类。
基于互联网上的消息来源,在StackOverflow上,我已经走到了这一步:
def getmethod(attrname):
def _getmethod(self):
return getattr(self, "__"+attrname).get()
return _getmethod
def setmethod(attrname):
def _setmethod(self, value):
return getattr(self, "__"+attrname).set(value)
return _setmethod
class Metaclass(type):
def __new__(cls, name, based, attrs):
ndict = {}
for attr in attrs:
if isinstance(attrs[attr], Field):
ndict['__'+attr] = attrs[attr]
ndict[attr] = property(getmethod(attr), setmethod(attr))
return super(Metaclass, cls).__new__(cls, name, based, ndict)
class Model(six.with_metaclass(Metaclass)):
foo = CharField()
def __init__(self, *args, **kwargs):
pass
class Field(object):
def __init__(self, required=False, *args, **kwargs):
self.required = required
self.name = None
def set(self, value):
self.value = value
def get(self):
return self.value
def set_name(self, name):
self.name = name
class CharField(Field):
def __init__(self, required=False, max_length=0, min_length=0, *args, **kwargs):
self.max_length = max_length
self.min_length = min_length
super(CharField, self).__init__(required, args, kwargs)
现在我创建了Model
class Product(Model):
name = CharField()
并创建Product
if __name__ == '__main__':
p = Product()
这很好用。
我甚至可以添加或更改产品名称
if __name__ == '__main__':
p = Product()
p.name = "Another beautiful product"
但是,当我想使用name
作为参数时:
if __name__ == '__main__':
p = Product(name="Another beautiful product")
引发错误:TypeError: object() takes no parameters
调试时我可以看到创建了Metaclass的实例,但是当到达行return super(Metaclass, cls).__new__(cls, name, based, ndict)
时会出现错误。
有人可以帮助我吗?
答案 0 :(得分:0)
在您的代码中,您创建了一组有趣的协作Field
类,可以将其作为丰富的自动属性。
(*见下文)。
但是您的元类代码只担心Field属性,并且不会自动执行类实例化时传递的参数。当您编写Product(name="my name")
之类的代码时,“name ='my name'”参数会传递到类的__init__
方法和__new__
方法中。通常是Python特殊情况__init__
和__new__
,因此未声明__init__
时__new__
的参数不会传递给基类(object
)。可能使用six.with_metaclass
正在破坏这种机制 - 而你的name
正在进入object
的构造函数(这会触发你所看到的错误)。
您可以通过自定义__new__
(或__init__
来解决这个问题,如果您可以完全消除元类,请参阅下文) - 只需使用您的**kwargs
参数,设置任何字段发送的地方,调用对象的__new__
没有那些无关的参数:
class Model(object):
def __init__(self, **kwargs):
for key, value in kwargs.items():
if isinstance(getattr(self.__class__, key, None), Field):
setattr(self, key, value)
(见下文 - 如果你仍然需要一个“六”的元类,你可能需要
将此代码写在__new__
中,而不是__init__
)
(*)现在 - 除了你的主要问题 - 因为你需要比Python的属性允许更丰富的属性,你不应该使用它 - 看看Descriptor Protocol如何工作 - 基本上,通过创建你的定义名为Field
和__get__
的方法的__set__
类,您可以从字段中获得所需的其他功能,而无需根据该功能(对于{{1}根据需要调用自动工作,您仍然需要自定义基础__init__
或元类