@property defx(self): """I'm the 'x' property.""" print("getter of x called") return self._x
@x.setter defx(self, value): print("setter of x called") self._x = value
@x.deleter defx(self): print("deleter of x called") del self._x
c = C() c.x = 'foo'# setter called foo = c.x # getter called del c.x # deleter called
Ответ 2
Каков pythonic способ использования геттеров и сеттеров?
"Питонический" способ заключается в том, чтобы не использовать "геттеры" и "установщики", а использовать простые атрибуты, как показано в вопросе, и del для удаления (но имена изменены для защиты невинных ... встроенных элементов):
value = 'something'
obj.attribute = value value = obj.attribute del obj.attribute
Если позже вы захотите изменить настройку и получение, вы можете сделать это без необходимости изменять пользовательский код, используя property декоратор:
classObj: """property demo""" # @property # first decorate the getter method defattribute(self): # This getter method name is *the* name return self._attribute # @attribute.setter # the property decorates with `.setter` now defattribute(self, value): # name, e.g. "attribute", is the same self._attribute = value # the "value" name isn't special # @attribute.deleter # decorate with `.deleter` defattribute(self): # again, the method name is the same del self._attribute
(Каждое использование декоратора копирует и обновляет предыдущий объект property , поэтому обратите внимание, что вы должны использовать одно и то же имя для каждой функции / метода set, get и delete.)
После определения выше, исходная настройка, получение и удаление кода одинаковы:
obj = Obj() obj.attribute = value the_value = obj.attribute del obj.attribute
Во-первых, приведенное выше не работает, потому что вы не предоставляете аргумент для экземпляра, на который было бы установлено свойство (обычно self), который был бы:
classObj:
defset_property(self, property, value): # don't do this ... defget_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 Декоратор предназначен для создания геттеров и сеттеров.
Например, мы могли бы изменить поведение настройки, чтобы наложить ограничения на устанавливаемое значение:
@protected_value.setter defprotected_value(self, value): if acceptable(value): # e.g. type or range check self._protected_value = value
В общем, мы хотим избежать использования property и просто использовать прямые атрибуты.
Это то, чего ожидают пользователи Python. Следуя правилу наименьшего удивления, вы должны попытаться дать своим пользователям то, чего они ожидают, если у вас нет очень веских причин для обратного.
Демонстрация
Допустим, нам нужно, чтобы защищенный атрибут нашего объекта был целым числом от 0 до 100 включительно, и предотвратить его удаление с помощью соответствующих сообщений, информирующих пользователя о его правильном использовании:
classProtective(object): """protected property demo""" # def__init__(self, start_protected_value=0): self.protected_value = start_protected_value # @property defprotected_value(self): return self._protected_value # @protected_value.setter defprotected_value(self, value): if value != int(value): raise TypeError("protected_value must be an integer") if0 <= value <= 100: self._protected_value = int(value) else: raise ValueError("protected_value must be " + "between 0 and 100 inclusive") # @protected_value.deleter defprotected_value(self): raise AttributeError("do not delete, protected_value can be set to 0")
(Обратите внимание, что это __init__ относится к self.protected_value, но методы свойств относятся к self._protected_value. Это делается для того, чтобы __init__ использовать свойство через общедоступный API, гарантируя, что оно "защищено".)
И использование:
>>> 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 0and100 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 0and100 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 создают копии исходного свойства. Это позволяет подклассам корректно изменять поведение, не изменяя поведение родительского класса.
classObj: """property demo""" # @property defget_only(self): return self._attribute # @get_only.setter defget_or_set(self, value): self._attribute = value # @get_or_set.deleter defget_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_... - для этого и существуют свойства.
Ответ 3
In [1]: classtest(object): def__init__(self): self.pants = 'pants' @property defp(self): return self.pants @p.setter defp(self, value): self.pants = value * 2 ....: In [2]: t = test() In [3]: t.p Out[3]: 'pants' In [4]: t.p = 10 In [5]: t.p Out[5]: 20
Ответ 4
Использование @property и @attribute.setter помогает вам не только использовать "питоновский" способ, но и проверять достоверность атрибутов как при создании объекта, так и при его изменении.
@name.setter defname(self, new_name): iftype(new_name) == str: #type checking for name property self._name = new_name else: raise Exception("Invalid value for name")
Таким образом вы фактически "скрываете" _name атрибут от разработчиков клиентов, а также выполняете проверки типа свойства name . Обратите внимание, что при следовании этому подходу даже во время инициализации вызывается установщик. Итак:
p = Person(12)
Приведет к:
Exception: Invalid value for name
Но:
>>>p = person('Mike') >>>print(p.name) Mike >>>p.name = 'George' >>>print(p.name) George >>>p.name = 2.3# Causes an exception