Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Свойства Python: Введение в ООП

Представим, что у нас есть такой класс:

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'

Сеттеры часто используют для того, чтобы проверить корректность нового значения или произвести какие-то его преобразования перед фактическим сохранением в другие атрибуты.

Делитер

Похожим на объявление сеттера образом объявляется и делитер (deleter). Этот метод вызывается тогда, когда происходит отбрасывание старого значения свойства, например — при присваивании свойству нового значения. Deleter позволяет напоследок сделать что-то необходимое, например, закрыть открытый ранее файл или сетевое соединение, словом, "прибрать за собой".

Не то чтобы делитеры часто приходится объявлять явно, ведь Python и сам неплохо справляется с закрытием тех же файлов, когда файловые объекты перестают быть нужны и уничтожаются сборщиком мусора. Но о самой возможности знать стоит, поэтому я её и описал.

Декоратор 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 может пригодиться, если вы будете делать свои декораторы, реализующие какие-то особенные свойства.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
30 июня 10 месяцев

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»