The fields['key'] = 'value'
runs before the metaclass machinery kicks in.
class foo(object):
var1 = 'bar'
def foobar(self):
pass
when python hits the class
statement, it enters a new local namespace.
it evaluates the var1 = 'bar'
statement. this is equivalent to locals()['var1'] = 'bar'
it then evaluates the def foobar
statement. this is equivalent to locals()['var'] = the result of compiling the function
It then passes locals()
, along with the classname, the inherited classes and the metaclass to the metaclasses __new__
method. In the example case, the metaclass is simply type
.
It then exits the new local namespace and sticks the class object returned from __new__
in the outer namespace with the name foo
.
Your code works when you use A.fields
because the class A
has already been created and the above process has hence been executed with your Meta
installing fields
in A
.
You can't use super().fields
because the classname B
is not defined to pass to super at the time that super would run. that is that you would need it to be super(B).fields
but B
is defined after class creation.
Update
Here's some code that will do what you want based on your reply 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