Metaclass __init__中setattr()的意外行为

时间:2018-06-07 16:32:51

标签: python python-3.x metaclass

这是一个基本Metaclass打算提供自定义__init__

class D(type):
    def __init__(cls, name, bases, attributes):
        super(D, cls).__init__(name, bases, attributes)
        for hook in R.registered_hooks:
            setattr(cls, hook.__qualname__, hook)

for循环遍历函数列表,为每个函数调用setattr(cls, hook.__qualname__, hook)

这是一个使用上述元类的类:

class E(metaclass=D):
    def __init__(self):
        pass

奇怪的事情:

E().__dict__   prints {}

但是当我打电话

`E.__dict__   prints {'f1': <function f1 at 0x001>, 'f2': <function f2 at 0x002>}`

我希望将这些功能作为属性添加到实例属性,因为__init__为类提供了自定义初始化,但似乎属性已添加到类属性

这是什么原因以及如何在此方案中向实例添加属性?谢谢!

3 个答案:

答案 0 :(得分:3)

它们作为实例属性添加。但是,实例是,作为元类的实例。您没有为__init__定义E方法;您正在定义type用于初始化E本身的方法。

如果您只想将属性添加到E的实例中,那么您的工作级别是错误的;你应该定义一个mixin并使用多重继承。

答案 1 :(得分:2)

要在对象__init__的实例化时调用obj,您需要定义type(obj).__init__。通过使用元类,您定义了type(type(obj)).__init__。你的工作水平不对。

在这种情况下,您只需要继承,而不是元类。

class D():
    def __init__(self, *args):
        print('D.__init__ was called')

class E(D):
    pass

e = E() # prints: D.__init__ was called

请注意,您仍会发现e.__dict__为空。

print(e.__dict__) # {}

这是因为你的实例没有属性,方法在类中存储和查找。

答案 2 :(得分:2)

如果您正在使用元类,则意味着您正在更改类的定义,这会间接影响该类实例的属性。假设你真的想这样做:

您正在创建自定义__init__方法。与正常实例化一样,在已创建的对象上调用__init__。通常,当您使用元类执行有意义的操作时,您希望在实例化之前添加到类的dict中,这是在__new__方法中完成的,以避免可能出现的问题在子类中。看起来应该是这样的:

class D(type):

    def __new__(cls, name, bases, dct):
        for hook in R.registered_hooks:
            dct[hook.__qualname__] = hook
        return super(D, cls).__new__(cls, name, bases, dct)

这不会修改该类的__dict__实例,但实例的属性解析也会查找类属性。因此,如果您在元类中添加foo作为E的属性,E().foo将返回预期值,即使它在E().__dict__中不可见。