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] 中不可见=].
这是一个基本的 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] 中不可见=].