Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the class access variables from its metaclass when calling class __init__? [closed]

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

when I executed Myclass(**r), this class called fields from MyMeta, not from Myclass. What happens in this process? Why isn't it using fields from 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) 
like image 692
Jack Avatar asked Feb 27 '26 07:02

Jack


1 Answers

It's not clear why you need a metaclass here, you probably don't. But for the sake of the exercise, let's examine what it going on.

The scope of Python's function is defined like this

locals > closure > globals > builtins

Never will a function lookup in the namespace of the scope where it was called. Notice that in this case, even defining MyClass.__init__ would not work since class methods must access their class namespace through attributes.

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

In particular, this means your init method will find fields in the body of MyMeta.__new__, which is its closure.

Although, note that the namespace of a class is passed as fourth argument to the MyMeta.__new__ method, here attr. So, you can find Myclass.fields at attr['fields'] in MyMeta.__new__.

Example

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)

Output

kwargs: {'baz': 2}
MyMeta.__new__.fields: {'foo': 0}
attr["fields"]: {'bar': 1}
like image 149
Olivier Melançon Avatar answered Mar 01 '26 21:03

Olivier Melançon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!