__get__
, __set__
и __delete__
. При назначении дескриптора атрибуту класса, эти методы будут автоматически вызываться при доступе, изменении или удалении атрибута соответственно.__setattr__(self, name, value)
в классе. Этот метод вызывается при каждой попытке присвоить значение атрибуту объекта. Внутри метода можно выполнить нужные действия перед присвоением значения. Важно вызывать super().__setattr__(name, value)
, чтобы избежать бесконечной рекурсии.__setattr__
:
class MyClass:
def __setattr__(self, name, value):
print(f"Атрибут {name} изменен на {value}")
super().__setattr__(name, value)
obj = MyClass()
obj.x = 10 # Выведет: Атрибут x изменен на 10
Для создания методов, которые вызываются автоматически при изменении атрибутов объекта в Python, можно использовать несколько подходов:
__get__
, __set__
и __delete__
.
class ValidatedString:
def __init__(self, storage_name):
self.storage_name = storage_name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.storage_name]
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError("Значение должно быть строкой")
instance.__dict__[self.storage_name] = value
class MyClass:
my_string = ValidatedString('__my_string')
def __init__(self, my_string):
self.my_string = my_string # Используем дескриптор для установки значения
# Пример использования
obj = MyClass("Hello")
print(obj.my_string) # Output: Hello
try:
obj.my_string = 123
except ValueError as e:
print(e) # Output: Значение должно быть строкой
В этом примере ValidatedString
- дескриптор. При присвоении значения атрибуту my_string
объекта MyClass
, вызывается метод __set__
дескриптора, позволяющий выполнять валидацию или другие действия.
__setattr__
: Метод __setattr__
перехватывает все попытки присвоения значения атрибуту объекта. Это дает полный контроль над процессом присвоения, но требует аккуратности, чтобы избежать бесконечной рекурсии.
class MyClass:
def __init__(self, x, y):
self._x = x # Используем другое имя для хранения значения
self._y = y
def __setattr__(self, name, value):
if name == 'x':
print(f"Изменяем атрибут x на {value}")
self.__dict__['_x'] = value # Обращаемся к __dict__ напрямую, чтобы избежать рекурсии
elif name == 'y':
print(f"Изменяем атрибут y на {value}")
self.__dict__['_y'] = value
else:
super().__setattr__(name, value) # Для остальных атрибутов используем стандартное поведение
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@property
def y(self):
return self._y
@y.setter
def y(self, value):
self._y = value
# Пример использования
obj = MyClass(10, 20)
obj.x = 100 # Output: Изменяем атрибут x на 100
obj.y = 200 # Output: Изменяем атрибут y на 200
Важно использовать self.__dict__[name] = value
внутри __setattr__
, чтобы избежать рекурсивного вызова __setattr__
.
@property_name.setter
) вызываются при изменении значения атрибута.
class MyClass:
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
@x.setter
def x(self, value):
print(f"Атрибут x изменен на {value}")
self._x = value
# Пример использования
obj = MyClass(10)
obj.x = 100 # Output: Атрибут x изменен на 100
Свойства удобны для простого перехвата операций чтения и записи атрибутов, но они не перехватывают присвоения напрямую в __dict__
.
Выбор между этими подходами зависит от конкретных потребностей и сложности задачи. Дескрипторы предоставляют наиболее гибкий и мощный механизм, но требуют больше понимания. __setattr__
дает полный контроль, но может быть сложен в реализации. Свойства - наиболее простой и интуитивно понятный подход для простых случаев.