Декоратор property в Python
Введение | |
Предпосылки | |
Пример | |
setter | |
deleter | |
Полный код примера | |
Похожие статьи |
Введение
Это продолжение статьи
«Классы»
из раздела
«ООП в Python»
.
Для лучшего понимания этого материала пригодятся знания из следующих статей:
Методы
,
Класс методы
,
Статические методы
,
super()
,
Декораторы
,
isinstance()
Партнёром этой статьи будем считать моего хостинг-провайдера beget.com
Предпосылки
Рассмотрим простой класс
class Employee: def __init__(self, first, last): self.first = first self.last = last self.email = first + '.' + last + '@beget.com' def fullname(self): return f'{self.first} {self.last}' emp_1 = Employee("Ivan", "Petrov") print(emp_1.first) print(emp_1.email) print(emp_1.fullname())
python property_deco.py
Ivan Ivan.Petrov@beget.com Ivan Petrov
Изменим только атрибут first у emp_1 и посмотрим как преобразится вывод
class Employee: def __init__(self, first, last): self.first = first self.last = last self.email = first + '.' + last + '@beget.com' def fullname(self): return f'{self.first} {self.last}' emp_1 = Employee("Ivan", "Petrov") # Change the attribute emp_1.first = 'Andrei' print(emp_1.first) print(emp_1.email) print(emp_1.fullname())
python property_deco.py
Andrei Ivan.Petrov@beget.com Andrei Petrov
Как видите, email остался прежним, как-будто работника по-прежнему зовут Иван. Это атрибут был создан вместе с объектом и
больше его никто не трогал.
fullname обновился, так как этот метод каждый раз переиспользует текущие атрибуты.
Что делать если нужно автоматически обновлять email?
Если убрать атрибут email и создать вместо него метод email по аналогии с fullname это заработает, но все, кто пользовался
этим классом должны будут изменить свой код. Это нарушает обратную совместимость.
Обратите внимание на вызов метода - нужно везде добавить ()
class Employee: def __init__(self, first, last): self.first = first self.last = last # self.email = first + "." + last + "@beget.com" def fullname(self): return f"{self.first} {self.last}" def email(self): return f"{self.first}.{self.last}@beget.com" emp_1 = Employee("Ivan", "Petrov") # Change the attribute emp_1.first = "Andrei" print(emp_1.first) print(emp_1.email()) print(emp_1.fullname())
python property_deco.py
Andrei Andrei.Petrov@beget.com Andrei Petrov
Оставить и метод и атрибут с тем же название нельзя - получим ошибку TypeError: 'str' object is not callable
Andrei Traceback (most recent call last): File "/home/andrei/python/property_deco.py", line 21, in <module> print(emp_1.email()) TypeError: 'str' object is not callable
Пример
Если написать метод email() и добавить к нему декоратор @property обратная совместимость не пострадает.
class Employee: def __init__(self, first, last): self.first = first self.last = last def fullname(self): return f"{self.first} {self.last}" @property def email(self): return f"{self.first}.{self.last}@beget.com" emp_1 = Employee("Ivan", "Petrov") # Change the attribute emp_1.first = "Andrei" print(emp_1.first) print(emp_1.email) print(emp_1.fullname())
python property_deco.py
Andrei Andrei.Petrov@beget.com Andrei Petrov
setter
Добавим декоратор @property к обоим методам и попробуем задать fullname
class Employee: def __init__(self, first, last): self.first = first self.last = last @property def fullname(self): return f"{self.first} {self.last}" @property def email(self): return f"{self.first}.{self.last}@beget.com" emp_1 = Employee("Ivan", "Petrov") # Change the attribute emp_1.fullname = "Andrei Olegovich" print(emp_1.first) print(emp_1.email) print(emp_1.fullname)
python property_deco.py
Traceback (most recent call last): File "/home/andrei/python/property_deco.py", line 21, in <module> emp_1.fullname = "Andrei Olegovich" AttributeError: can't set attribute
Избавиться от ошибки поможет декоратор setter
@fullname.setter def fullname(self, name): first, last = name.split(' ') self.first = first self.last = last emp_1 = Employee("Ivan", "Petrov") # Change the attribute emp_1.fullname = "Andrei Olegovich" print(emp_1.first) print(emp_1.email) print(emp_1.fullname)
python property_deco.py
Andrei Andrei.Petrov@beget.com Andrei Petrov
deleter
Удалить атрибут лучше всего с помощью декоратора deleter
@fullname.deleter def fullname(self): print("Delete Name!") self.first = None self.last = None emp_1.fullname = "Andrei Olegovich" print(emp_1.first) print(emp_1.email) print(emp_1.fullname) del emp_1.fullname print(emp_1.fullname)
python property_deco.py
Andrei Andrei.Olegovich@beget.com Andrei Olegovich Delete Name! None None
Полный код примера
class Employee: def __init__(self, first, last): self.first = first self.last = last @property def email(self): return f"{self.first}.{self.last}@beget.com" @property def fullname(self): return f"{self.first} {self.last}" @fullname.setter def fullname(self, name): first, last = name.split(' ') self.first = first self.last = last @fullname.deleter def fullname(self): print("Delete Name!") self.first = None self.last = None emp_1 = Employee("Ivan", "Petrov") # Change the attribute emp_1.fullname = "Andrei Olegovich" print(emp_1.first) print(emp_1.email) print(emp_1.fullname) del emp_1.fullname print(emp_1.fullname)
OOP in Python | |
Classes | |
Methods | |
class variables | |
class methods | |
Static Methods | |
Inheritance | |
Special Methods | |
property decorator | |
Python | |
Functions | |
super() |