Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Объявление Setter в Python
Setter — это метод, который позволяет присваивать значение свойству (property) с дополнительной логикой. В Python это реализуется через декоратор @property и @<property_name>.setter.
1. Базовый пример setter
class Person:
def __init__(self, name: str):
self._name = name # Приватное хранилище
@property
def name(self) -> str:
"""Getter — чтение значения."""
return self._name
@name.setter
def name(self, value: str) -> None:
"""Setter — установка значения с валидацией."""
if not isinstance(value, str):
raise TypeError("name должен быть строкой")
if len(value) < 2:
raise ValueError("name должен быть не менее 2 символов")
self._name = value
# Использование
person = Person("John")
print(person.name) # John (getter)
person.name = "Jane" # Setter с валидацией
print(person.name) # Jane
# Валидация срабатывает
try:
person.name = "A" # ValueError
except ValueError as e:
print(f"Ошибка: {e}")
2. Полный цикл: getter, setter, deleter
class Temperature:
def __init__(self, celsius: float):
self._celsius = celsius
@property
def celsius(self) -> float:
"""Getter."""
return self._celsius
@celsius.setter
def celsius(self, value: float) -> None:
"""Setter с валидацией."""
if not isinstance(value, (int, float)):
raise TypeError("Температура должна быть числом")
if value < -273.15:
raise ValueError("Температура ниже абсолютного нуля")
self._celsius = value
@celsius.deleter
def celsius(self) -> None:
"""Deleter — удаление значения."""
print("Температура удалена")
self._celsius = None
@property
def fahrenheit(self) -> float:
"""Вычисляемое свойство (только для чтения)."""
return self._celsius * 9/5 + 32
# Использование
temp = Temperature(25)
print(temp.celsius) # 25 (getter)
print(temp.fahrenheit) # 77.0 (вычисляемое)
temp.celsius = 30 # Setter
print(temp.celsius) # 30
del temp.celsius # Deleter
print(temp.celsius) # None
3. Валидация в setter
class BankAccount:
def __init__(self, balance: float):
self._balance = balance
@property
def balance(self) -> float:
return self._balance
@balance.setter
def balance(self, value: float) -> None:
"""Установка баланса с проверкой."""
if value < 0:
raise ValueError("Баланс не может быть отрицательным")
self._balance = value
def withdraw(self, amount: float) -> None:
"""Снятие денег (правильный способ)."""
self.balance = self.balance - amount # Использует setter для валидации
account = BankAccount(1000)
print(account.balance) # 1000
account.withdraw(100)
print(account.balance) # 900
try:
account.balance = -50 # ValueError
except ValueError as e:
print(f"Ошибка: {e}")
4. Защита от несанкционированного доступа
class SecureUser:
def __init__(self, username: str, password: str):
self._username = username
self._password = password # Никогда не выставляем password как property
@property
def username(self) -> str:
return self._username
@username.setter
def username(self, value: str) -> None:
if not (3 <= len(value) <= 20):
raise ValueError("Username должен быть 3-20 символов")
self._username = value
def set_password(self, old_password: str, new_password: str) -> bool:
"""Правильный способ изменения пароля."""
if self._password != old_password:
raise ValueError("Неверный старый пароль")
if len(new_password) < 8:
raise ValueError("Пароль должен быть не менее 8 символов")
self._password = new_password
return True
def check_password(self, password: str) -> bool:
"""Проверка пароля (не возвращаем сам пароль)."""
return self._password == password
user = SecureUser("alice", "secure123")
print(user.username) # alice
user.username = "alice_new"
print(user.username) # alice_new
# Пароль не может быть прочитан как свойство
print(hasattr(user, "password")) # False
5. Setter с побочными эффектами
class Widget:
def __init__(self, width: int, height: int):
self._width = width
self._height = height
self._area = width * height
@property
def width(self) -> int:
return self._width
@width.setter
def width(self, value: int) -> None:
"""Setter обновляет площадь при изменении ширины."""
if value <= 0:
raise ValueError("Ширина должна быть положительной")
self._width = value
self._update_area() # Побочный эффект
@property
def height(self) -> int:
return self._height
@height.setter
def height(self, value: int) -> None:
if value <= 0:
raise ValueError("Высота должна быть положительной")
self._height = value
self._update_area() # Побочный эффект
def _update_area(self) -> None:
"""Приватный метод для обновления площади."""
self._area = self._width * self._height
print(f"Площадь обновлена: {self._area}")
@property
def area(self) -> int:
"""Только для чтения."""
return self._area
widget = Widget(10, 20)
print(widget.area) # 200
widget.width = 15 # Площадь обновлена: 300
print(widget.area) # 300
6. Setter в наследованиях
class Animal:
def __init__(self, age: int):
self._age = age
@property
def age(self) -> int:
return self._age
@age.setter
def age(self, value: int) -> None:
if value < 0:
raise ValueError("Возраст не может быть отрицательным")
self._age = value
class Dog(Animal):
@property
def age(self) -> int:
"""Переопределяем getter."""
return self._age
@age.setter
def age(self, value: int) -> None:
"""Переопределяем setter с дополнительной логикой."""
if value < 0:
raise ValueError("Возраст не может быть отрицательным")
if value > 30:
raise ValueError("Собака не может быть старше 30 лет")
self._age = value
dog = Dog(5)
dog.age = 10 # OK
print(dog.age) # 10
try:
dog.age = 50 # ValueError
except ValueError as e:
print(f"Ошибка: {e}")
7. Setter с логированием
class LoggedProperty:
def __init__(self, name: str, initial_value):
self._name = name
self._value = initial_value
self._history = [initial_value]
@property
def value(self):
return self._value
@value.setter
def value(self, new_value) -> None:
"""Setter логирует все изменения."""
print(f"[LOG] {self._name}: {self._value} -> {new_value}")
self._value = new_value
self._history.append(new_value)
@property
def history(self):
return self._history
prop = LoggedProperty("count", 0)
prop.value = 5 # [LOG] count: 0 -> 5
prop.value = 10 # [LOG] count: 5 -> 10
print(prop.history) # [0, 5, 10]
8. Setter для дефолтных значений
class Configuration:
_defaults = {"debug": False, "timeout": 30}
def __init__(self):
self._config = self._defaults.copy()
@property
def debug(self) -> bool:
return self._config["debug"]
@debug.setter
def debug(self, value: bool) -> None:
if not isinstance(value, bool):
raise TypeError("debug должен быть bool")
self._config["debug"] = value
@property
def timeout(self) -> int:
return self._config["timeout"]
@timeout.setter
def timeout(self, value: int) -> None:
if value <= 0:
raise ValueError("timeout должен быть положительным")
self._config["timeout"] = value
config = Configuration()
print(config.debug) # False
config.debug = True
print(config.debug) # True
config.timeout = 60
print(config.timeout) # 60
Лучшие практики
✅ Делай так:
- Используй
@propertyдля чтения и@<name>.setterдля записи - Валидируй данные в setter
- Логируй важные изменения
- Защищай критичные данные (пароли, токены)
- Документируй поведение setter
❌ Не делай так:
- Не используй setter для побочных эффектов без логирования
- Не скрывай сложную логику в setter (это запутывает)
- Не меняй поведение setter между версиями без警告
- Не делай setter async (используй методы вместо этого)
Синтаксис запоминается как:
@propertyдля getter@<имя>.setterдля setter@<имя>.deleterдля deleter (опционально)
Setter — это мощный инструмент для инкапсуляции и валидации данных.