Python Tutorial

Python Variable

Python Operators

Python Sequence

Python String

Python Flow Control

Python Functions

Python Class and Object

Python Class Members (properties and methods)

Python Exception Handling

Python Modules

Python File Operations (I/O)

What is a descriptor, Python descriptor

In Python, descriptors are a powerful feature that allows you to customize the behavior of how an object's attributes are accessed, modified, and deleted. Descriptors are classes that define one or more of the following special methods: __get__, __set__, and __delete__.

In this tutorial, we will learn how to create a descriptor in Python and use it to control access to an attribute.

Example: Creating a Descriptor

Let's create a descriptor called PositiveNumber that ensures that a number is always positive:

class PositiveNumber:
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Value cannot be negative")
        self.value = value

    def __delete__(self, instance):
        del self.value

In this example, we define the PositiveNumber descriptor with the following methods:

  1. The __get__() method is called when the descriptor is accessed. It takes the instance and the owner class as arguments and returns the value of the descriptor.
  2. The __set__() method is called when the descriptor is modified. It takes the instance and the new value as arguments. In this case, the method checks if the new value is negative and raises a ValueError if it is. Otherwise, it sets the value of the descriptor.
  3. The __delete__() method is called when the descriptor is deleted. It takes the instance as an argument and deletes the value of the descriptor.

Using the Descriptor in a Class

Now, let's use the PositiveNumber descriptor in a class called Product:

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = PositiveNumber(price)

In this example, we create a Product class with a name attribute and a price attribute that uses the PositiveNumber descriptor. The PositiveNumber descriptor ensures that the price attribute is always positive.

Example: Using the Descriptor

Let's create instances of the Product class and see how the descriptor works:

# Create a Product instance
product1 = Product("Laptop", 1000)

# Access the price attribute
print(product1.price)  # Output: 1000

# Modify the price attribute
product1.price = 1200
print(product1.price)  # Output: 1200

# Try to set a negative price
try:
    product1.price = -500
except ValueError as e:
    print(e)  # Output: Value cannot be negative

In this example, we create a Product instance and access, modify, and try to set a negative value for the price attribute. The PositiveNumber descriptor ensures that the price attribute is always positive and raises a ValueError if a negative value is attempted.

In conclusion, Python descriptors provide a way to control access to an object's attributes by customizing their behavior. Descriptors can be used to enforce data validation, implement computed properties, and more. By understanding and using descriptors, you can create more powerful and flexible classes in Python.

  1. Creating custom descriptors in Python:

    • Description: Create custom descriptors by defining a class with __get__, __set__, and __delete__ methods.
    • Code:
      class CustomDescriptor:
          def __get__(self, instance, owner):
              return instance._value
      
          def __set__(self, instance, value):
              if value < 0:
                  raise ValueError("Value must be non-negative")
              instance._value = value
      
      class MyClass:
          descriptor = CustomDescriptor()
      
          def __init__(self, value):
              self._value = value
      
  2. Built-in descriptor types in Python:

    • Description: Python provides built-in descriptor types like property, classmethod, and staticmethod.
    • Code:
      class MyClass:
          @property
          def my_property(self):
              return self._value
      
          @my_property.setter
          def my_property(self, value):
              if value < 0:
                  raise ValueError("Value must be non-negative")
              self._value = value
      
  3. Getter and setter methods in descriptors:

    • Description: Getter and setter methods define how to get and set attribute values using descriptors.
    • Code:
      class DescriptorWithGetterSetter:
          def __get__(self, instance, owner):
              return instance._value
      
          def __set__(self, instance, value):
              if value < 0:
                  raise ValueError("Value must be non-negative")
              instance._value = value
      
      class MyClass:
          descriptor = DescriptorWithGetterSetter()
      
          def __init__(self, value):
              self._value = value
      
  4. Decorators and descriptors in Python:

    • Description: Decorators can be used to simplify the usage of descriptors. The property decorator is an example of a descriptor.
    • Code:
      class MyClass:
          @property
          def my_property(self):
              return self._value
      
          @my_property.setter
          def my_property(self, value):
              if value < 0:
                  raise ValueError("Value must be non-negative")
              self._value = value