Python: __new__ time in metaclass

The following code does not compile; He says

NameError: name 'fields' is not defined

in the last line. Is it because __new__ not called until the fields assignment is reached? What should I do?

 class Meta(type): def __new__(mcs, name, bases, attr): attr['fields'] = {} return type.__new__(mcs, name, bases, attr) class A(metaclass = Meta): def __init__(self, name): pass class B(A): fields['key'] = 'value' 

EDIT:

I found that this is not a timing issue; this is a problem with hiding the name. Works A.fields if I write A.fields .

I would like to know why I cannot use fields or super().fields .

+4
source share
1 answer

fields['key'] = 'value' started before the metaclass mechanism is started.

 class foo(object): var1 = 'bar' def foobar(self): pass 

when python gets into the class statement, it enters the new local namespace.

  • computes the var1 = 'bar' operator. this is equivalent to locals()['var1'] = 'bar'

  • then computes the def foobar statement. this is equivalent to locals()['var'] = the result of compiling the function

  • It then passes locals() , as well as the class name, inherited classes, and the metaclass to the __new__ metaclass __new__ . In the case of the example, the metaclass is simply type .

  • It then exits the new local namespace and places the class object returned from __new__ in an external namespace named foo .

Your code works when you use A.fields because class A has already been created and the above process has been completed with your installation of Meta fields in A

You cannot use super().fields , because the name of the class B not defined to go to super at the time super starts. that is, you need super(B).fields , but B is defined after the class is created.

Update

Here is the code that will do what you want based on your answer to my comment on the question.

 def MakeFields(**fields): return fields class Meta(type): def __new__(mcs, name, bases, attr): for base in bases: if hasattr(base, 'fields'): inherited = getattr(base, 'fields') try: attr['fields'].update(inherited) except KeyError: attr['fields'] = inherited except ValueError: pass return type.__new__(mcs, name, bases, attr) class A(metaclass=Meta): fields = MakeFields(id='int',name='varchar') class B(A): fields = MakeFields(count='int') class C(B): pass class Test(object): fields = "asd" class D(C, Test): pass print C.fields print D.fields 
+4
source

All Articles