Вопрос-Ответ

What's the pythonic way to use getters and setters?

Каков pythonic способ использования геттеров и сеттеров?

Я делаю это следующим образом:

def set_property(property,value):  
def get_property(property):

или

object.property = value  
value = object.property

Каков pythonic способ использования геттеров и сеттеров?

Переведено автоматически
Ответ 1

Попробуйте это: Свойство Python

Пример кода:

class C(object):
def __init__(self):
self._x = None

@property
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x

@x.setter
def x(self, value):
print("setter of x called")
self._x = value

@x.deleter
def x(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 декоратор:

class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(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

Вам следует избегать этого:


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):
"""protected property demo"""
#
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")

(Обратите внимание, что это __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 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_... - для этого и существуют свойства.

Ответ 3
In [1]: class test(object):
def __init__(self):
self.pants = 'pants'
@property
def p(self):
return self.pants
@p.setter
def p(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 помогает вам не только использовать "питоновский" способ, но и проверять достоверность атрибутов как при создании объекта, так и при его изменении.

class Person(object):
def __init__(self, p_name=None):
self.name = p_name

@property
def name(self):
return self._name

@name.setter
def name(self, new_name):
if type(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
python