为什么类在调用类__init__时从其元类访问变量?

时间:2019-03-24 10:50:07

标签: python metaclass

r={'a':6} 
c = Myclass(**r) 
out: a 

当我执行Myclass(**r)时,该类从fields而不是MyMeta被称为Myclass。在这个过程中会发生什么?为什么不使用fields中的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) 

1 个答案:

答案 0 :(得分:2)

目前尚不清楚为什么需要在这里使用元类,您可能不需要。但是为了进行练习,让我们检查一下正在进行的事情。

Python函数的作用域定义如下

locals > closure > globals > builtins

永远不会在调用它的作用域的命名空间中进行函数查找。请注意,在这种情况下,即使定义MyClass.__init__也行不通,因为类方法必须通过属性访问其类名称空间。

class SomeClass:
    foo = 0
    def __init__(self):
        self.foo # works
        foo # raises NameError

特别是,这意味着您的init方法将在fields的主体(即其闭包)中找到MyMeta.__new__

不过,请注意,类的名称空间作为第四个参数传递给MyMeta.__new__方法,此处为attr。因此,您可以在Myclass.fields的{​​{1}}处找到attr['fields']

示例

MyMeta.__new__

输出

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)