Code Explanation:
1. Defining the descriptor class
class D:
This defines a class D.
Objects of this class will be used as descriptors.
2. Defining __get__
def __get__(self, obj, owner):
return "desc"
__get__ makes D a descriptor.
Parameters:
self → the descriptor object (D()).
obj → the instance accessing the attribute (b).
owner → the class of the instance (Box).
Whenever this descriptor is used to access an attribute, it returns "desc".
Since D defines only __get__, it is a non-data descriptor.
3. Defining another class
class Box:
This defines a class named Box.
4. Attaching the descriptor to the class
x = D()
x is a class attribute.
It is assigned an instance of D, so x becomes a descriptor-managed attribute.
5. Creating an instance
b = Box()
This creates an object b of class Box.
6. Manually adding an instance attribute
b.__dict__["x"] = "inst"
This directly inserts "x": "inst" into the instance’s namespace.
Now b has its own instance attribute x.
This bypasses normal attribute assignment syntax.
7. Accessing the attribute
print(b.x)
Let’s see how Python resolves b.x:
Attribute lookup order (important here)
Data descriptors (have __get__ + __set__)
Instance attributes (b.__dict__)
Non-data descriptors (only __get__)
Class attributes
D is a non-data descriptor (only __get__).
Python checks b.__dict__ before non-data descriptors.
b.__dict__ contains:
{"x": "inst"}
So "inst" is returned.
The descriptor’s __get__ is not called.
✅ Final Output
inst

0 Comments:
Post a Comment