Зарегистрируйтесь, чтобы продолжить обучение

Свойства 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'

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

Делитер

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


Дополнительные материалы

  1. Свойства

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

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

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

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

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Программирование на Python, Разработка веб-приложений и сервисов используя Django, проектирование и реализация REST API
10 месяцев
с нуля
Старт 26 декабря

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

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

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

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»