Представим, что у нас есть такой класс:
class Person:
def __init__(self, name, surname):
self.name = name
self.surname = surname
Что нам делать, если мы захотим иметь возможность получить еще и полное имя (имя+фамилию) объекта?
Мы могли бы в инициализаторе добавить строчку self.full_name = name + ' ' + surname
. Но если впоследствии имя или фамилия поменяются, полное имя устареет, и его нужно будет не забыть поменять.
Также мы могли бы добавить метод с именем вроде get_full_name
, который бы возвращал полное имя. Но и такой вариант не идеален, ведь нам хочется работать с полным именем, как с простым атрибутом-переменной.
Оказывается, что в Python есть средство, позволяющее получить атрибут, значение которого вычисляется динамически, то есть во время обращения к атрибуту. Речь идет о свойствах.
Добавим в класс свойство и посмотрим на его использование:
class Person:
def __init__(self, name, surname):
self.name = name
self.surname = surname
@property
def full_name(self):
return self.name + ' ' + self.surname
tom = Person('Thomas', 'Smith')
tom.full_name # 'Thomas Smith'
full_name
выглядит как утка, то есть как атрибут! Но вычисляется динамически. И если мы поменяем name
, то full_name
также изменится:
tom.name = 'Alice'
tom.full_name # 'Alice Smith'
Как вы можете видеть, свойство объявляется как метод без параметров (кроме self
, естественно), декорированный с помощью property
. Такой метод, возвращающий динамически вычисляемое значение, называется геттером (getter).
Сеттер
Атрибутам пространства имен можно присваивать значения, но что делать, если атрибут — свойство? Если вы попытаетесь присвоить значение свойству, у которого один лишь getter, вы получите ошибку "AttributeError: can't set attribute".
Чтобы иметь возможность присвоить значение свойству, нужно использовать сеттер (setter). Сеттер — это тоже метод, который принимает новое значение для атрибута и как-то его обрабатывает. Чтобы метод стал сеттером, его тоже нужно соответствующим образом декорировать. Если у вас уже есть геттер, вы можете сделать так:
class Person:
def __init__(self, name, surname):
self.name = name
self.surname = surname
@property
def full_name(self):
return self.name + ' ' + self.surname
# сеттер для свойства full_name
@full_name.setter
def full_name(self, new):
self.name, self.surname = new.split(' ')
Теперь свойству full_name
можно присваивать новое полное имя. В итоге оно будет разделено по пробелу на две части, первая из которых станет новым именем, а вторая — фамилией. Пример:
tom = Person('Thomas', 'Smith')
tom.full_name # 'Thomas Smith'
tom.full_name = 'Alice Cooper'
tom.name # 'Alice'
tom.surname # 'Cooper'
Сеттеры часто используют для того, чтобы проверить корректность нового значения или произвести какие-то его преобразования перед фактическим сохранением в другие атрибуты.
Делитер
Кроме геттеров и сеттеров, в Python существует еще один важный метод для управления атрибутами классов — это делитеры (deleter). Этот метод предоставляет дополнительный уровень контроля над тем, как атрибуты класса удаляются.
Делитер — это особый метод в классе, который вызывается при удалении атрибута. Как правило, удаление атрибута - это нечастая операция, и в большинстве случаев мы можем обойтись без явного определения делитера. Тем не менее, зная о его существовании, мы можем контролировать процесс удаления атрибутов, например, очищать связанные ресурсы или выполнять некоторую логику очистки.
В нашем примере с классом Person
, если мы решим реализовать делитер для свойства full_name
, то его можно использовать для удаления имени и фамилии. Давайте посмотрим, как это работает:
class Person:
def __init__(self, name, surname):
self.name = name
self.surname = surname
@property
def full_name(self):
return self.name + ' ' + self.surname
@full_name.setter
def full_name(self, new):
self.name, self.surname = new.split(' ')
@full_name.deleter
def full_name(self):
print("Удаляем имя и фамилию!")
self.name = None
self.surname = None
Теперь при удалении свойства full_name
будет вызываться наш делитер, который устанавливает свойствам name
и surname
значение None
, тем самым "удаляя" эти значения. Пример:
tom = Person('Thomas', 'Smith')
tom.full_name # 'Thomas Smith'
del tom.full_name # Удаляем имя и фамилию
tom.name # None
tom.surname # None
Использование делитера особенно полезно в случаях, когда необходимо управлять удалением важных или связанных данных, или выполнять освобождение ресурсов (как, например, закрытие файлов или сетевых соединений) в момент уничтожения объекта или его свойств.
Декоратор property
Если посмотреть в документацию к декоратору property
, то можно увидеть такую сигнатуру:
property(fget=None, fset=None, fdel=None, doc=None)
Первые три аргумента позволяют задать getter, setter и deleter, а аргумент doc
позволяет указать docstring. В такой форме property
удобно использовать, когда вы уже имеете готовые функции, которые хотите просто "упаковать" в свойство:
def get_full_name(self):
...
def set_full_name(self, new):
...
class Person:
...
full_name = property(
fget=get_full_name,
fset=set_full_name,
doc='A full name of person'
)
Также обычный вызов property
может пригодиться, если вы будете делать свои декораторы, реализующие какие-то особенные свойства.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.