Metaclass 中 setattr() 的意外行为 __init__

Unexpected behavior of setattr() inside Metaclass __init__

这是一个基本的 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:

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__ 为 Class 提供了自定义初始化,但似乎就像属性被添加到 Class attributes.

这是什么原因,在这种情况下如何向实例添加属性?谢谢!

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

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

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

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

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__) # {}

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

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

您正在创建自定义 __init__ 方法。与正常实例化一样,对已创建的对象调用 __init__。通常当你用 metaclasses 做一些有意义的事情时,你会想在实例化之前添加到 class 的字典中,这是在 __new__ 方法中完成的以避免问题这可能会出现在 subclassing 中。它应该看起来像这样:

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)

这不会修改 class 实例的 __dict__,但实例的属性解析也会查找 class 属性。因此,如果您在 metaclass 中添加 foo 作为 E 的属性,E().foo 将 return 预期值,即使它在 [=18] 中不可见=].