Python2(有六个)元类和带参数的字段
Python2 (with six) metaclass and Fields with parameters
我正在创建一个应该与字段一起使用的元类。
根据 Internet 上的资源和 Whosebug 上的资料,我已经走到这一步了:
元类
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
调试时我可以看到元类的实例已创建,但在到达行 return super(Metaclass, cls).__new__(cls, name, based, ndict)
时出现错误。
有人可以帮我吗?
在您的代码中,您创建了一组有趣的协作 Field
class,它们可以用作丰富的自动属性。
(* 请参阅下文)。
但是您的元class 代码只关心 Field 属性,而不会自动执行传递给 class 实例化的参数。当您编写类似 Product(name="my name")
的代码时,"name='my name'" 参数会传递到 class 的 __init__
方法和 __new__
方法中。通常 Python 特殊情况 __init__
和 __new__
以便在未声明 [=14=] 时 __init__
的参数不会传递给基 class ( object
)。可能 six.with_metaclass
的使用破坏了这种机制 - 而你的 name
正在进入 object
的构造函数(这会触发你看到的错误)。
你可以通过自定义 __new__
来解决这个问题(或者 __init__
如果你完全可以摆脱 metaclass,见下文)- 只需消耗你的 **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)
(见下文——如果你仍然需要一个带有 "six" 的元class,你可能需要
将此代码写在 __new__
中,而不是 __init__
)
(*) 现在 - 除了你的主要问题 - 因为你需要比 Python 的 属性 允许的更丰富的属性,你不应该使用它 - 看看如何Descriptor Protocol 有效 - 基本上,通过创建 Field
class 定义名为 __get__
和 __set__
的方法,您可以从字段中获得所需的附加功能,而无需对于 metaclass(对于该功能 - 要使 __init__
调用根据需要自动工作,您仍然需要自定义基础 __init__
或 metaclass)
我正在创建一个应该与字段一起使用的元类。
根据 Internet 上的资源和 Whosebug 上的资料,我已经走到这一步了:
元类
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
调试时我可以看到元类的实例已创建,但在到达行 return super(Metaclass, cls).__new__(cls, name, based, ndict)
时出现错误。
有人可以帮我吗?
在您的代码中,您创建了一组有趣的协作 Field
class,它们可以用作丰富的自动属性。
(* 请参阅下文)。
但是您的元class 代码只关心 Field 属性,而不会自动执行传递给 class 实例化的参数。当您编写类似 Product(name="my name")
的代码时,"name='my name'" 参数会传递到 class 的 __init__
方法和 __new__
方法中。通常 Python 特殊情况 __init__
和 __new__
以便在未声明 [=14=] 时 __init__
的参数不会传递给基 class ( object
)。可能 six.with_metaclass
的使用破坏了这种机制 - 而你的 name
正在进入 object
的构造函数(这会触发你看到的错误)。
你可以通过自定义 __new__
来解决这个问题(或者 __init__
如果你完全可以摆脱 metaclass,见下文)- 只需消耗你的 **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)
(见下文——如果你仍然需要一个带有 "six" 的元class,你可能需要
将此代码写在 __new__
中,而不是 __init__
)
(*) 现在 - 除了你的主要问题 - 因为你需要比 Python 的 属性 允许的更丰富的属性,你不应该使用它 - 看看如何Descriptor Protocol 有效 - 基本上,通过创建 Field
class 定义名为 __get__
和 __set__
的方法,您可以从字段中获得所需的附加功能,而无需对于 metaclass(对于该功能 - 要使 __init__
调用根据需要自动工作,您仍然需要自定义基础 __init__
或 metaclass)