Автор оригинала: Scott Robinson.
Часто считается лучшей практикой создавать геттеры и сеттеры для открытых свойств класса. Многие языки позволяют реализовать это по-разному, либо с помощью функции (например, person.getName ()
), либо с помощью специфичной для языка конструкции get
или set
. В Python это делается с помощью @property
.
В этой статье я опишу декоратор свойств Python, который, возможно, использовался с синтаксисом @decorator
:
class Person(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name @property def full_name(self): return self.first_name + ' ' + self.last_name @full_name.setter def full_name(self, value): first_name, last_name = value.split(' ') self.first_name = first_name self.last_name = last_name @full_name.deleter def full_name(self): del self.first_name del self.last_name
Это способ Python создавать геттеры, сеттеры и delete (или мутаторные методы ) для свойства в классе.
В этом случае @property
decorator делает так, что вы вызываете метод full_name(self)
, как будто это просто обычное свойство, тогда как на самом деле это метод, содержащий код, который будет выполняться при установке свойства.
Использование геттера/сеттера/делетера, подобного этому, дает нам довольно много преимуществ, некоторые из которых я перечислил здесь:
- Проверка: Перед установкой внутреннего свойства вы можете проверить, что предоставленное значение соответствует некоторым критериям, и заставить его выдать ошибку, если это не так.
- Ленивая загрузка: Ресурсы могут лениво загружаться откладывать работу до тех пор, пока она действительно не понадобится, экономя время и ресурсы
- Абстракция: Геттеры и сеттеры позволяют абстрагировать внутреннее представление данных. Как и в нашем примере выше, например, имя и фамилия хранятся отдельно, но геттеры и сеттеры содержат логику, которая использует имя и фамилию для создания полного имени.
- Отладка: Поскольку методы мутатора могут инкапсулировать любой код, он становится отличным местом для перехвата при отладке (или протоколировании) вашего кода. Например, вы можете регистрировать или проверять каждый раз, когда изменяется значение свойства.
Python достигает этой функциональности с помощью декораторов, которые являются специальными методами, используемыми для изменения поведения другой функции или класса. Чтобы описать, как работает декоратор @property
, давайте рассмотрим более простой декоратор и то, как он работает внутри.
Декоратор-это просто функция, которая принимает другую функцию в качестве аргумента и добавляет к ее поведению, обернув ее. Вот простой пример:
# decorator.py def some_func(): print 'Hey, you guys' def my_decorator(func): def inner(): print 'Before func!' func() print 'After func!' return inner print 'some_func():' some_func() print '' some_func_decorated = my_decorator(some_func) print 'some_func() with decorator:' some_func_decorated()
Запуск этого кода дает вам:
$ python decorator.py some_func(): Hey, you guys some_func() with decorator: Before func! Hey, you guys After func!
Как вы можете видеть, функция my_decorator()
динамически создает новую функцию для возврата с помощью функции ввода, добавляя код, который будет выполняться до и после запуска исходной функции.
свойство
decorator реализовано с помощью шаблона, аналогичного функции my_decorator
. Используя синтаксис Python @decorator
, он получает украшенную функцию в качестве аргумента, как и в моем примере: some_func_decorated(some_func)
.
Итак, возвращаясь к моему первому примеру, этот код:
@property def full_name_getter(self): return self.first_name + ' ' + self.last_name
Примерно эквивалентно этому:
def full_name_getter(self): return self.first_name + ' ' + self.last_name full_name = property(full_name_getter)
Обратите внимание, что я изменил некоторые имена функций для ясности.
Затем, позже, когда вы захотите использовать @full_name.setter
как мы делаем в этом примере, то, что вы действительно вызываете, это:
def full_name_setter(self, value): first_name, last_name = value.split(' ') self.first_name = first_name self.last_name = last_name full_name = property(full_name_getter) full_name = full_name.setter(full_name_setter)
Теперь этот новый объект full_name
(экземпляр объекта property
) имеет как методы getter, так и setter.
Чтобы использовать их с нашим классом, Person
, объект property
действует как дескриптор, что означает, что он имеет свой собственный __get__() , __set__() и __delete__() методы. Методы __get__()
и __set__()
срабатывают на объекте при извлечении или установке свойства, а __delete__()
срабатывают при удалении свойства с помощью del
.
Таким образом, person.full_name
запускает метод __set__ ()
, который был унаследован от object
. Это подводит нас к важному моменту – ваш класс должен наследовать от object
для того, чтобы это работало . Таким образом, такой класс не сможет использовать свойства сеттера, так как он не наследуется от объекта
:
class Person: pass
Благодаря property
эти методы теперь соответствуют нашим full_name_getter
и full_name_setter
методам сверху:
full_name.fget is full_name_getter # True full_name.fset is full_name_setter # True
fget
и fset
теперь обернуты .__get__()
и .__set__ ()
соответственно.
И, наконец, к этим объектам дескриптора можно получить доступ, передав ссылку на наш класс, Person
:
>>> person = Person('Billy', 'Bob') >>> >>> full_name.__get__(person) Billy Bob >>> >>> full_name.__set__(person, 'Timmy Thomas') >>> >>> person.first_name Timmy >>> person.last_name Thomas
Это, по сути, то, как свойства работают под поверхностью.