Unexpected behavior of read only attributes and super

Steven Bethard steven.bethard at gmail.com
Wed Dec 7 12:29:15 EST 2005


Samuel M. Smith wrote:
> On 06 Dec, 2005, at 20:53, Steven Bethard wrote:
>> You can always shadow class-level attributes in the instance dict.
>> (That's what you were doing.)  If you want to (try to) replace an
>> attribute in the class dict, you need to use the class object, not an
>> instance object.
> 
> I guess that's where my understanding breaks down. I thought the only  
> way to access class attributes was by
> calling the class directly as your example indicates but __iter__ is  a 
> class attribute that I can access from the instance
> at least to read it. So what determines which class attributes get  
> copied to the instance and which ones don't?

When "reading" an attribute, Python looks through the namespaces in the 
order (instance, type).  So if the attribute exists in the instance, the 
instance-level value is returned.  If the attribute does not exist in 
the instance, but does exist in the class, then the class-level value is 
returned:

 >>> class C(object):
...     x = 1
...
 >>> inst = C()
 >>> inst.x
1
 >>> C.x
1
 >>> class C(object):
...     x = 1
...     def __init__(self):
...         self.x = 2
...
 >>> inst = C()
 >>> inst.x
2
 >>> C.x
1

When "writing" an attribute (i.e. using the assignment statement), 
Python does not try to do any namespace searching.  Thus if you use the 
instance in an assignment statement, then it is the instance's 
attributes that get modified, and if you use the class in an assignment 
statement, then it is the class's attributes that get modififed:

 >>> class C(object):
...     pass
...
 >>> inst = C()
 >>> inst.x = 1
 >>> C.x
Traceback (most recent call last):
   File "<interactive input>", line 1, in ?
AttributeError: type object 'C' has no attribute 'x'
 >>> inst.x
1
 >>> class C(object):
...     pass
...
 >>> inst = C()
 >>> C.x = 1
 >>> inst.x
1
 >>> C.x
1

HTH,

STeVe

P.S. Note that there is an additional complication resulting from the 
fact that functions are descriptors:

 >>> class C(dict):
...     pass
...
 >>> C.__iter__
<slot wrapper '__iter__' of 'dict' objects>
 >>> C().__iter__
<method-wrapper object at 0x00E74A10>

Even though the C instance is accessing the __iter__ function on the 
class, it gets back a different value because descriptors return 
different values depending on whether they are accessed from a class or 
an instance.  I don't think you need to understand this to solve your 
problem though, so I won't go into any more details unless you think it 
would be helpful.



More information about the Python-list mailing list