6

Let's say I define class A:

>>> class A:
...     a = 1
...     class SubA:
...         sub_a = { 'a': 1, 'b': 1}

Then I define class B that inherits from A:

>>> class B(A):
...     pass

Now, check __dict__ of A and __dict__ of B:

>>> A.__dict__
{'a': 1, '__module__': '__builtin__', '__doc__': None, 'SubA': <class __builtin_ _.SubA at 0x02CAA3E8>}
>>> B.__dict__
{'__module__': '__builtin__', '__doc__': None}

Somehow, B.__dict__ contains neither 'a' nor 'SubA'. Now if we do:

>>> A.a
1
>>> B.a
1

>>> A.SubA
<class __builtin__.SubA at 0x02CAA3E8>
>>> B.SubA
<class __builtin__.SubA at 0x02CAA3E8>

First question: why B.__dict__ does not contain 'a' and 'SubA'? Second question: Why B.a and B.SubA give the expected results, although neither 'a' nor 'SubA' is in B's __dict__?

Thanks!

jazzblue
  • 2,411
  • 4
  • 38
  • 63
  • essentially it attempts to resolve the name in the child class first, then each of its super classes then each superclasses superclass etc. until it resolves or it doesnt ... at least thats what I think happens – Joran Beasley May 07 '14 at 23:07

2 Answers2

9

@bgporter has given a good explanation of the behaviour, I'll just go into why a little:

If your class variable was in B.__dict__, how would it function? Each subclass would have its own value for a, independent of the value for A.a - this is not what you would expect. A class variable should exist once - in that class.

Instead, Python does a lookup on the class and if it doesn't exist, then looks up to its base classes - note that means it is possible to shadow a class variable in a subclass.

Ry-
  • 218,210
  • 55
  • 464
  • 476
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • Good point. Now if I want to override class members: B.a=2 does the job and now B.__dict__ has 'a'. However, B.SubA.sub_a = A.SubA.sub_a.copy() does not help, since if I update B.SubA.sub_a, A.SubA.sub_a gets updated too. Is it because SubA has to be overriden prior to that? – jazzblue May 07 '14 at 23:17
  • Yes. `B.SubA` and `A.SubA` are the same thing in that case. In general, inner types are rarely seen in Python as they don't offer much functionality or value. – Gareth Latty May 07 '14 at 23:18
  • I found that you might need to use it sometimes, like in Django ModelForms when you have a Meta subclass and you want to override its members. – jazzblue May 07 '14 at 23:26
5

That's how Python's object model works:

A class has a namespace implemented by a dictionary object. Class attribute references are translated to lookups in this dictionary, e.g., C.x is translated to C.__dict__["x"] (although for new-style classes in particular there are a number of hooks which allow for other means of locating attributes). When the attribute name is not found there, the attribute search continues in the base classes.

bgporter
  • 35,114
  • 8
  • 59
  • 65