Python 元类_init中setattr()的意外行为__

Python 元类_init中setattr()的意外行为__,python,python-3.x,metaclass,Python,Python 3.x,Metaclass,下面是一个基本的元类,旨在提供一个自定义的\uuuuu init\uuuuuu: 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)

下面是一个基本的
元类
,旨在提供一个自定义的
\uuuuu init\uuuuuu

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.\uu qualname\uuu,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>}`
`E.uu dict_uuu打印{'f1':,'f2':}`
我希望将这些函数作为属性添加到实例属性,因为
\uuuu init\uuuu
为类提供了自定义初始化,但似乎属性已添加到类属性


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

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


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

要在对象的实例化上调用
obj
,您需要定义
类型(obj)。\uuu init\uu
。通过使用元类,您定义了
type(type(obj))。\uuuu init\uuuu
。你在错误的层次上工作

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

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

class E(D):
    pass

e = E() # prints: D.__init__ was called
请注意,您仍然会发现
e.\uu dict\uu
为空

print(e.__dict__) # {}

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

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

您正在创建一个自定义的
\uuuu init\uuu
方法。与普通实例化一样,对已创建的对象调用
\uuuuu init\uuuuu
。通常,当您使用元类执行一些有意义的操作时,您会希望在实例化之前添加到类的dict中,这是在
\uuuuu new\uuuu
方法中完成的,以避免子类化过程中可能出现的问题。它应该是这样的:

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)
这不会修改该类实例的
\uuuu dict\uuu
,但实例的属性解析也会查找类属性。因此,如果在元类中添加
foo
作为
E
的属性,
E().foo
将返回预期值,即使它在
E()中不可见