Ответ
Метод __set__(self, instance, value) является частью протокола data descriptor (дескриптора данных) в Python. Он перехватывает операцию присваивания значения атрибуту, позволяя выполнить валидацию, преобразование типов или другую логику перед сохранением.
Он вызывается автоматически, когда происходит попытка присвоить значение атрибуту, управляемому дескриптором.
Параметры:
self: экземпляр самого дескриптора.instance: экземпляр класса, в котором находится атрибут (например,MyClass()).value: присваиваемое значение.
Пример: Дескриптор для неотрицательных чисел
Чтобы дескриптор знал имя атрибута, с которым он работает, используется метод __set_name__ (Python 3.6+).
class NonNegative:
def __set_name__(self, owner, name):
# Сохраняем имя атрибута, например, 'price' или 'quantity'
self.private_name = '_' + name
def __get__(self, instance, owner):
# Возвращаем значение из приватного атрибута экземпляра
return getattr(instance, self.private_name)
def __set__(self, instance, value):
# Логика валидации перед присваиванием
if value < 0:
raise ValueError(f"Значение не может быть отрицательным.")
# Сохраняем значение в приватный атрибут экземпляра
setattr(instance, self.private_name, value)
class Product:
price = NonNegative()
quantity = NonNegative()
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
# Использование
item = Product(100, 5)
print(f"Цена: {item.price}, Количество: {item.quantity}") # Цена: 100, Количество: 5
item.price = 200 # OK
# Эта строка вызовет ValueError
try:
item.quantity = -1
except ValueError as e:
print(e)
Ключевые моменты:
- Data Descriptor: Наличие метода
__set__(или__delete__) делает дескриптор дескриптором данных, который имеет более высокий приоритет, чем атрибуты в словаре экземпляра (__dict__). - Валидация: Это основной сценарий использования
__set__— проверка данных на соответствие определенным правилам. - Инкапсуляция: Позволяет скрыть внутреннюю логику хранения данных за простым интерфейсом атрибута.
Ответ 18+ 🔞
А, ну это же про тот самый __set__, который как злобный охранник на входе в клуб! Смотри, чувак, чтобы ты не пытался впихнуть в атрибут какую-нибудь дичь, этот метод всё проверит и либо пропустит, либо нахуй пошлёт с ошибкой.
Вот представь: у тебя есть класс Product с атрибутами price и quantity. И ты такой: "О, сейчас я сделаю item.price = -100, пусть магазин платит покупателям!" А __set__ тебе в ответ: "Нихуя, дружок-пирожок, отрицательная цена — это пиздец какой-то. Иди нахуй со своим Value Error'ом".
Вот как это выглядит под капотом, когда ты пытаешься что-то присвоить:
# Ты пишешь это:
item.price = 200
# А Python делает примерно это за кулисами:
Product.__dict__['price'].__set__(item, 200)
Вот тебе живой пример, чтобы мозг не взорвался. Делаем дескриптор для проверки, что число не отрицательное. Главная фишка — метод __set_name__, который запоминает, как его назвали в классе, чтобы потом знать, в какой приватный атрибут экземпляра всё складывать.
class NonNegative:
def __set_name__(self, owner, name):
# Запоминаем имя, но с подвохом. 'price' станет '_price' в экземпляре.
# Хитрая жопа, да? Прячем данные, чтобы народ не шарился.
self.private_name = '_' + name
def __get__(self, instance, owner):
# Когда кто-то спрашивает значение, мы достаём его из потайного кармана.
return getattr(instance, self.private_name)
def __set__(self, instance, value):
# А вот и наш охранник на входе! Основная мясорубка.
if value < 0:
# Поймал на хуйне! Вызываем скандал.
raise ValueError(f"Значение не может быть отрицательным.")
# Всё чисто, проходи, клади свои данные в сейф.
setattr(instance, self.private_name, value)
class Product:
# Вот они, наши контролёры. Прикидываются обычными атрибутами класса.
price = NonNegative()
quantity = NonNegative()
def __init__(self, price, quantity):
# При создании объекта присваивание тоже идёт через __set__!
# Так что валидация работает сразу, с первого дня.
self.price = price
self.quantity = quantity
# Поехали тестить
item = Product(100, 5)
print(f"Цена: {item.price}, Количество: {item.quantity}") # Всё ок, печатаем
item.price = 200 # Нормас, проходи
# А вот сейчас будет шоу
try:
item.quantity = -1 # Пытаемся впихнуть минус один товар. Гениально!
except ValueError as e:
print(e) # И получаем по ебалу от дескриптора: "Значение не может быть отрицательным."
Запомни три вещи, чтобы не выглядеть как манда с ушами:
- Data Descriptor — это сила. Если у дескриптора есть
__set__или__delete__, он становится главнее любого атрибута в__dict__экземпляра. Он перехватывает ВСЕ попытки что-то записать. Это овердохуища власти. - Валидация — его родная стихия. Его основная работа — стоять на страже и не пускать всякую хуйню типа отрицательных цен, пустых строк или ебучих
Noneтуда, где они не нужны. - Инкапсуляция на максимум. Снаружи класс
Productвыглядит просто и чисто: простоpriceиquantity. А вся грязная работа по проверке и хранению в_priceи_quantityспрятана внутри дескриптора. Красота, ёпта!
В общем, __set__ — это твой личный телохранитель для атрибутов класса. Хочешь, чтобы данные всегда были в порядке — заведи такого.