sag*_*ian 6 python metaclass python-3.x
我创建了一个定义该__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
Run Code Online (Sandbox Code Playgroud)
当我运行它时,for_prepare类 A 定义中的关键字参数重新出现在__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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,该for_prepare项目已从字典中删除,并且传递到的字典__new__是传递到的同一对象以及从中弹出__prepare__该项目的同一对象,但在其中重新出现了!为什么从字典中删除的关键字又被添加回来?for_prepare__new__
传递给new 的dict 与传递给prepare的对象是同一个对象
不幸的是,这就是你错的地方。
Python只回收相同的对象id。
如果你在里面创建一个新的字典,__prepare__你会注意到 id 的kwds变化__new__。
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')
x = {} # <<< create a new dict
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
Run Code Online (Sandbox Code Playgroud)
输出:
in M.__prepare__:
metaclass=<class '__main__.M'>
name='A'
bases=()
kwds={'for_prepare': 'xyz'}
id(kwds)=2595838763072
arg popped for prepare: xyz
end of prepare: kwds={} id(kwds)=2595838763072
in M.__new__:
metaclass=<class '__main__.M'>
name='A'
bases=()
ns={'__module__': '__main__', '__qualname__': 'A'}
kwds={'for_prepare': 'xyz'}
id(kwds)=2595836298496 # <<< id has changed now
Traceback (most recent call last):
File "d:\nemetris\mpf\mpf.test\test_so4.py", line 22, in <module>
class A(metaclass=M, for_prepare='xyz'):
File "d:\nemetris\mpf\mpf.test\test_so4.py", line 19, in __new__
return super().__new__(metaclass, name, bases, ns, **kwds)
TypeError: A.__init_subclass__() takes no keyword arguments
Run Code Online (Sandbox Code Playgroud)