Python2(有六个)元类和带参数的字段

Posted

技术标签:

【中文标题】Python2(有六个)元类和带参数的字段【英文标题】:Python2 (with six) metaclass and Fields with parameters 【发布时间】:2017-05-05 14:12:29 【问题描述】:

我正在创建一个应该使用字段的元类。

基于互联网上的资源,在 *** 上我已经走到了这一步:

元类

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) 行时引发错误。

有人可以帮我吗?

【问题讨论】:

看来你的问题可以用descriptors更好地解决。 【参考方案1】:

在您的代码中,您创建了一组有趣的协作Field 类,可以使其作为丰富的自动属性工作。 (* 见下文)。

但是您的元类代码只担心 Field 属性,并且不会自动执行在类实例化时传递的参数。当您编写像Product(name="my name") 这样的代码时,“name='my name'”参数被传递到类的__init__ 方法和__new__ 方法中。通常是 Python 特殊情况 __init____new__,因此当 __new__ 未声明时,__init__ 的参数不会传递给基类 (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 是如何工作的 - 基本上,通过创建你的 @987654341 @ 名为 __get____set__ 的类定义方法,您可以从字段中获得所需的附加功能,而根本不需要元类(对于该功能 - 要让 __init__ 调用根据需要自动工作,您仍然需要自定义基础 __init__ 或元类)

【讨论】:

以上是关于Python2(有六个)元类和带参数的字段的主要内容,如果未能解决你的问题,请参考以下文章

参数化类和元类有啥区别(请使用 Python 中的代码示例)?

request发送带headers和带参数的请求

ActionScript 3 EventListener 和带参数的函数

MS Access 更新查询和带字符串参数的内部连接

requests不带参数的get请求和带get参数请求

有返回值和带参数的修饰器