Какой питонный способ использовать геттеры и сеттеры?
"Pythonic" - это не для использования "getters" и "setters", но для использования простых атрибутов, как показано в вопросе, и del
для разыменования (но имена изменяются на защитить невинных ... встроенных):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Если позже вы захотите изменить настройку и получить, вы можете сделать это без необходимости изменять код пользователя, используя декоратор property
:
class Obj:
"""property demo"""
#
@property
def attribute(self): # implements the get - this name is *the* name
return self._attribute
#
@attribute.setter
def attribute(self, value): # name must be the same
self._attribute = value
#
@attribute.deleter
def attribute(self): # again, name must be the same
del self._attribute
(Каждый декоратор копирует и обновляет предыдущий объект свойства, поэтому обратите внимание, что вам, вероятно, следует использовать одно и то же имя для каждого набора, получения и удаления функции / метода.)
После определения вышеизложенного исходная настройка, получение и удаление одинаковы:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Вам следует избегать этого:
def set_property(property,value):
def get_property(property):
Во-первых, вышеприведенное не работает, потому что вы не предоставляете аргумент для экземпляра, для которого будет установлено свойство (обычно self
), которое будет:
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
Во-вторых, это дублирует назначение двух специальных методов, __setattr__
и __getattr__
.
В-третьих, у нас также есть встроенные функции setattr
и getattr
.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
Декоратор @property
предназначен для создания геттеров и сеттеров.
Например, мы могли бы изменить поведение настройки, чтобы наложить ограничения на устанавливаемое значение:
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
В общем, мы хотим избежать использования property
и просто использовать прямые атрибуты.
Это то, что ожидают пользователи Python. Следуя правилу наименьшего удивления, вы должны стараться дать своим пользователям то, что они ожидают, если у вас нет веских причин для обратного.
Демонстрация
Например, скажем, нам нужно, чтобы атрибут защищенного объекта был целым числом от 0 до 100 включительно, и предотвращать его удаление с соответствующими сообщениями, информирующими пользователя о его правильном использовании:
class Protective(object):
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
И использование:
>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
Имена имеют значение?
Да, они делают . .setter
и .deleter
делают копии оригинальной собственности. Это позволяет подклассам корректно изменять поведение без изменения поведения в родительском элементе.
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
Теперь, чтобы это работало, вы должны использовать соответствующие имена:
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
Я не уверен, где это было бы полезно, но вариант использования - если вы хотите получить, установить и / или удалить только свойство. Вероятно, лучше придерживаться семантически одного и того же свойства с таким же именем.
Заключение
Начните с простых атрибутов.
Если вам позже потребуется функциональность, связанная с настройкой, получением и удалением, вы можете добавить ее с помощью декоратора свойств.
Избегайте функций с именами set_...
и get_...
- вот для чего нужны свойства.