Here is some very similar code that might help you understand what's happening
class MyClass(object):
def __init__(self, data):
self.__dict__.update(data) # this would fail if I had self.a = a
@property
def a(self):
return 1
MyObject = MyClass({'a': 2})
print MyObject.a
print object.__getattribute__(MyObject, 'a')
print MyObject.__dict__['a']
What you're seeing is that the instance (the class) having both a descriptor (your property) on its class and an attribute of the same name. Normally, there are safeguards to protect this, but the way type
works goes around them.
So you have
print MyObject.a
The descriptor beats the __dict__
entry and the property
is called. This is because of the implementation of object.__getattribute__
, at least conceptually.
print object.__getattribute__(MyObject, 'a')
This is the same thing as saying MyObject.a
, except if object.__getattribute__
was overridden. __getattribute__
is where the behavior of trying descriptors first comes from.
print type.__getattribute__(MyObject, 'a')
This is the same thing as object.__getattribute__
because type
doesn't override __getattribute__
.
print MyObject.__dict__['a']
This looks it up in your __dict__
, which is the only place you stored 2
. This is just a dict object (maybe/almost), so it's not going to look stuff up anywhere else.
print MyObject().a
The way that attribute works, you're not accessing your type's attributes the same way you would directly. This is probably the part that doesn't have a super-intuitive answer