![]() |
VOOZH | about |
In Python, a descriptor is any object that implements at least one of the following methods: __get__(self, instance, owner), __set__(self, instance, value), or __delete__(self, instance). When a class defines any of these methods, its instances become descriptors. Descriptors act as intermediaries in attribute access, enabling custom behavior when attributes are read, written or deleted. Example:
Accessing value 100
Explanation: Desc class defines a __get__ method that runs automatically when its managed attribute is accessed. In class A, x is linked to Desc, so accessing a.x triggers __get__, printing a message and returning 100.
Attributes are usually accessed or modified directly. However, if you want to add extra logic such as validating values, logging access, or enforcing type rules, you can use descriptors. Descriptors let you control what happens when an attribute is accessed, set or deleted. They are useful for adding validation, tracking usage or reusing the same logic across multiple classes.
Example: In this example, we're creating a custom descriptor class Desc to manage access to the name attribute of another class A.
Vishakshi GFG
Explanation: Desc class uses __get__ to return _name and __set__ to assign it only if the value is a string otherwise, it raises a ValueError. In class A, name = Desc() links the descriptor to the name attribute. self.name = name triggers __set__ and a.name triggers __get__, ensuring name is always a valid string and safely managed.
There are two types of descriptors: data descriptors and non-data descriptors. Let’s understand each one individually.
A data descriptor is a class that defines both __get__ and __set__ (or __delete__). It overrides instance attributes with the same name, meaning it always controls how the value is accessed or modified. This makes it useful for adding validation, type checking, or custom logic when working with attributes.
Example:
10
Explanation:desc class with both __get__ and __set__ methods. Assigning x = desc() in class A lets the descriptor control x. On self.x = v, __set__ stores v in _val. Accessing obj.x calls __get__, returning _val. Thus, desc handles x’s storage and access.
A non-data descriptor is a class that defines only the __get__ method (without __set__ or __delete__). It can be overridden by instance attributes, meaning assigning a value directly to the instance will hide the descriptor. It's useful for read-only or computed attributes that don't need strict control.
Example:
from desc value
Explanation: Assigning y = desc() in class A makes y a non-data descriptor. Accessing obj.y calls __get__, returning "from desc". Assigning obj.y = "value" creates an instance attribute, overriding the descriptor. Further access returns the instance value.
property() function creates managed attributes in a class by binding getter, setter, and deleter methods. It enables controlled access to private variables, supporting encapsulation without altering attribute access syntax.
GeeksforGeeks GfG
Explanation: Class A has a private attribute _value, with the property() function binding getter, setter and deleter methods to the value property. This provides controlled access to _value, enabling attribute-like behavior for access, assignment and deletion, while encapsulating _value without direct exposure.
@property decorator is a more Pythonic approach to achieve the same result as property(). It simplifies the syntax for binding getter, setter and deleter methods directly to the class attribute, making the code cleaner and more readable while maintaining controlled access and encapsulation..
hello world
Explanation: class A uses the @property decorator to manage the private attribute _value with a getter, setter, and deleter. The getter retrieves the value, the setter updates it, and the deleter removes the attribute. This ensures controlled access and modification of _value.