我创建了一个元类,定义了__prepare__
方法,该方法应该在类定义中消耗特定的关键字,如下所示:
class M(type):
@classmethod
def __prepare__(metaclass, name, bases, **kwds):
print('in M.__prepare__:')
print(f' {metaclass=}\n {name=}\n'
f' {bases=}\n {kwds=}\n {id(kwds)=}')
if 'for_prepare' not in kwds:
return super().__prepare__(name, bases, **kwds)
arg = kwds.pop('for_prepare')
print(f' arg popped for prepare: {arg}')
print(f' end of prepare: {kwds=} {id(kwds)=}')
return super().__prepare__(name, bases, **kwds)
def __new__(metaclass, name, bases, ns, **kwds):
print('in M.__new__:')
print(f' {metaclass=}\n {name=}\n'
f' {bases=}\n {ns=}\n {kwds=}\n {id(kwds)=}')
return super().__new__(metaclass, name, bases, ns, **kwds)
class A(metaclass=M, for_prepare='xyz'):
pass
当我运行它时,在 A 类的定义中,for_prepare
关键字参数在 __new__
中再次出现(稍后在 __init_subclass__
中也有出现,导致出错):
$ python3 ./weird_prepare.py
in M.__prepare__:
metaclass=<class '__main__.M'>
name='A'
bases=()
kwds={'for_prepare': 'xyz'}
id(kwds)=140128409916224
arg popped for prepare: xyz
end of prepare: kwds={} id(kwds)=140128409916224
in M.__new__:
metaclass=<class '__main__.M'>
name='A'
bases=()
ns={'__module__': '__main__', '__qualname__': 'A'}
kwds={'for_prepare': 'xyz'}
id(kwds)=140128409916224
Traceback (most recent call last):
File "./weird_prepare.py", line 21, in <module>
class A(metaclass=M, for_prepare='xyz'):
File "./weird_prepare.py", line 18, in __new__
return super().__new__(metaclass, name, bases, ns, **kwds)
TypeError: __init_subclass__() takes no keyword arguments
如您所见,字典中的
for_prepare
项目已被移除,传递给__new__
的字典与传递给__prepare__
的相同,并且从其中弹出了for_prepare
项目,但在__new__
中它又出现了!为什么已经从字典中删除的关键词会重新添加回去呢?
__init_subclass__
应该总是以协作的方式编写,接收 **kwargs,并使用未处理的任何参数调用 super。不幸的是,__init_subclass__
显然必须设置为具有接受 KW 参数并消耗它们的元类的任何类。 - jsbueno