调用类__init__时,为什么类从其元类访问变量?
作者:互联网
r={'a':6}
c = Myclass(**r)
out: a
当我执行Myclass(** r)时,此类从MyMeta而不是Myclass调用字段.在这个过程中会发生什么?为什么不使用Myclass中的字段?
class MyMeta(type) :
def __new__(mcs,name,bases,attr) :
fields = {}
fields['a'] = 2
fields['b'] = 4
fields['c'] = 44
if '__init__' not in attr:
def init(self,**kwargs):
self.api = kwargs.pop('api', None)
for k,v in kwargs.items():
if k in fields :
print(v)
attr['__init__'] = init
return type.__new__(mcs,name,bases,attr)
class Myclass(metaclass = MyMeta ):
fields = {'c': 5}
def get(self):
print(4)
解决方法:
目前尚不清楚为什么在这里需要一个元类,您可能不需要.但是,为了进行练习,让我们检查一下正在进行的事情.
Python函数的范围是这样定义的
locals > closure > globals > builtins
永远不会在调用范围的名称空间中查找函数.请注意,在这种情况下,即使定义MyClass .__ init__也不起作用,因为类方法必须通过属性访问其类名称空间.
class SomeClass:
foo = 0
def __init__(self):
self.foo # works
foo # raises NameError
特别是,这意味着您的init方法将在MyMeta .__ new__的主体中找到字段,这是它的闭包.
虽然,请注意,将类的名称空间作为第四个参数传递给MyMeta .__ new__方法(此处为attr).因此,您可以在MyMeta .__ new__的attr [‘fields’]上找到Myclass.fields.
例
class MyMeta(type):
def __new__(mcs, name, bases, attr):
fields = {'foo': 0}
def init(self, **kwargs):
print('kwargs:', kwargs)
print('MyMeta.__new__.fields:', fields)
print('attr["fields"]:', attr['fields'])
attr['__init__'] = init
return type.__new__(mcs, name, bases, attr)
class Myclass(metaclass=MyMeta):
fields = {'bar': 1}
r = {'baz': 2}
c = Myclass(**r)
输出量
kwargs: {'baz': 2}
MyMeta.__new__.fields: {'foo': 0}
attr["fields"]: {'bar': 1}
标签:metaclass,python 来源: https://codeday.me/bug/20191211/2105538.html