lettura simple

The __set__() Method in Python

The special `__set__()` method in Python is part of the concept of "descriptors", which might be less familiar to beginners but is incredibly powerful when you need advanced control over how an object's attributes are accessed and modified.

What is a descriptor? In simple terms, a descriptor is an object that manages how an attribute is retrieved, set, or deleted. Python leverages special methods like `__get__()`, `__set__()`, and `__delete__()` to provide this functionality.

The `__set__()` method of a descriptor is automatically called whenever you attempt to assign a value to an attribute that the descriptor manages.

Think of it as a "gatekeeper" that says: "Whenever you try to modify this attribute, I’ll step in and decide what happens!"

It can either allow the change, modify it, or even block it entirely.

class Descriptor:
    def __set__(self, instance, value):
       # code
       pass

In this code:

  • Self is the descriptor itself
  • Instance is the object that contains the attribute
  • Value is the new value being assigned

Now, let’s see how to use this in practice with a real-world example.

Imagine you want to create a class representing a person, and you need to ensure that their age is always positive (since, of course, a negative age doesn't make any sense).

To enforce this, you can use the `__set__()` method within a descriptor class.

class PositiveAgeDescriptor:
    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Age cannot be negative!")
        instance.__dict__['age'] = value  # Store the attribute in the object
   
    def __get__(self, instance, owner):
        return instance.__dict__.get('age', None)

In this descriptor class, called 'PositiveAgeDescriptor,' the `__set__()` method is invoked whenever you try to assign a value to the `age` attribute.

Inside this method, it checks if the value is negative. If it is, it raises an error. If not, it stores the age in the object’s `__dict__`.

The descriptor also implements the `__get__()` method, which is called whenever you try to access the attribute’s value. In our case, we retrieve it from `__dict__`, the internal dictionary Python uses to store an object's attributes.

Next, let’s create a 'Person' class that utilizes the descriptor for its attributes.

class Person:
    age = PositiveAgeDescriptor()

Now, instantiate the Person class:

p = Person()

Assign a positive value to the 'age' attribute:

p.age = 25 

When you set the 'age' attribute, the object automatically calls the descriptor class `PositiveAgeDescriptor`, which checks if the value is negative via the '__set__()' method.

In this case, the value is positive, so it passes the validation, and the value is stored as expected.

Now, if you access the attribute, you’ll see the value you set:

print(p.age) 

25

But if you try to assign a negative value, the descriptor will raise a ValueError:

p.age = -5 

ValueError: Age cannot be negative!

So, think of a descriptor as an additional layer of control. If something’s not right—like a value that breaks the logic of your application—the descriptor steps in and stops the action.

In conclusion, while the `__set__()` method may seem a bit complex at first, it’s really just a way to apply custom behavior to attribute access.

You can use it to validate inputs, transform data, or even control how attributes are stored internally.




Report a mistake or post a question




FacebookTwitterLinkedinLinkedin