Your understanding is incorrect. x
most likely does not appear in the instance's dict at all; the descriptor object appears in the class's dict or the dict of one of the superclasses.
Let's use an example:
class Foo(object):
@property
def x(self):
return 0
def y(self):
return 1
x = Foo()
x.__dict__['x'] = 2
x.__dict__['y'] = 3
Foo.x
and Foo.y
are both descriptors. (Properties and functions both implement the descriptor protocol.)
When we access x.x
:
>>> x.x
0
We do not get the value from x
's dict. Instead, since Python finds a data descriptor by the name of x
in Foo.__dict__
, it calls
Foo.__dict__['x'].__get__(x, Foo)
and returns the result. The data descriptor wins over the instance dict.
On the other hand, if we try x.y
:
>>> x.y
3
we get 3, rather than a bound method object. Functions don't have __set__
or __delete__
, so the instance dict overrides them.
As for the new Part 2 to your question, descriptors don't function in the instance dict. Consider what would happen if they did:
class Foo(object):
@property
def bar(self):
return 4
Foo.bar = 3
If descriptors functioned in the instance dict, then the assignment to Foo.bar
would find a descriptor in Foo
's dict and call Foo.__dict__['bar'].__set__
. The __set__
method of the descriptor would have to handle setting the attribute on both the class and the instance, and it would have to tell the difference somehow, even in the face of metaclasses. There just isn't a compelling reason to complicate the protocol this way.