DEV Community

Customizing Lyf
Customizing Lyf

Posted on

Descriptor in Python


Descriptor: A descriptor is a special object in Python that lets you control what happens when you get, set, or delete an attribute on a class.
All in all they helps us to customize attribute access and behavior
e.g.

  • __get__(self, obj, objtype=None)
  • __set__(self, obj, value)
  • __delete__(self, obj)
  • __get_name__

Descriptors are the core behind:

  • @property
  • @staticmethod
  • @classmethod
  • and any custom attribute access logic!

There are two types of Descriptors

  1. Non-data Descriptors
class Q:
    def __get__(self, instance, owner):
        return "Yes"

class P:
    attr = Q()

p = P()
print(p.attr) # Output: Yes
Enter fullscreen mode Exit fullscreen mode
  1. Data Descriptors
class X:
    def __get__(self, obj, objcls):
        print(f"Getting value from {obj=} of type {objcls=}")
        if obj:
            return getattr(obj, "_find_x", "Nothing here")
        return getattr(objcls, "_find_x", "Nothing here")

    def __set__(self, obj, value):
        print(f"Setting {value=} on {obj=}")
        setattr(obj, "_find_x", value)

    def __delete__(self, obj):
        print(f"Deleting {obj} from find_x")
        delattr(obj, "_find_x")

class P:
    attr = X()

    def __init__(self, value):
        self.attr = value


print(P.attr) # Output: Getting value from obj=None of type objcls=<class '__main__.P'>
              # Nothing here

p = P(15) # Output: Setting value=15 on obj=<__main__.P object at 0x7e8eaded1d00>

print(p.attr) # Output: Getting value from obj=<__main__.P object at 0x7e8eaded1d00> of type objcls=<class '__main__.P'>
              # 15

print(p.__dict__) # {'_find_x': 15}

print(del p.attr) # Output: Deleting <__main__.P object at 0x7d4a4bcd5d00> from find_x
Enter fullscreen mode Exit fullscreen mode

Bonus:
__dict__ : It basically tells the attributes of the class in dictionary format.
It has two types:

  • Class Attributes: Attributes defined directly within a class are stored in class's __dict__.
class Q:
    a = 98

q = Q()
print(q.__dict__) # Output: {}
print(q.a)        # Output: 98
print(Q.__dict__) # Output: {..., 'a': 98, ...other attributes}
Enter fullscreen mode Exit fullscreen mode
  • Instance Attributes: Each instance has its own __dict__ that stores attributes unique to that instance.
class Q:
    def __init__(self):
        self.a = 98

q = Q()
print(q.__dict__) # Output: {'a': 98}
q.x = 23
print(q.__dict__) # Output: {'a': 98, 'x': 23}

print(Q.__dict__) # Output: {<will not contain a or x>}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)