Я делаю это так:
def set_property(property,value):
def get_property(property):
или
object.property = value
value = object.property
Я новичок в Python, так что я все еще изучаю синтаксис, и я хотел бы несколько советов по этому вопросу.
Я делаю это так:
def set_property(property,value):
def get_property(property):
или
object.property = value
value = object.property
Я новичок в Python, так что я все еще изучаю синтаксис, и я хотел бы несколько советов по этому вопросу.
Ответы:
Попробуйте это: свойство 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
._x
(который не является свойством, просто атрибут) обходят property
перенос. Только ссылки, чтобы .x
пройти property
.
Какой питонный способ использовать геттеры и сеттеры?
«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
(Каждое использование декоратора копирует и обновляет предыдущий объект свойства, поэтому обратите внимание, что вы должны использовать одно и то же имя для каждого набора, получения и удаления функции / метода.
После определения вышеизложенного исходная настройка, получение и удаление кода остаются прежними:
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_...
- вот для чего нужны свойства.
__init__
метод относится к методу , а к методу self.protected_value
getter и setters self._protected_value
. Не могли бы вы объяснить, как это работает? Я проверил ваш код, и он работает как есть - так что это не опечатка.
__init__
не так ли?
self.protected_value = start_protected_value
что на самом деле вызывает функцию-установщик; Я думал, что это было задание.
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
Использование @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
Проверьте @property
декоратор .
Вы можете использовать аксессоры / мутаторы (то есть @attr.setter
и @property
) или нет, но самое главное, чтобы быть последовательным!
Если вы используете @property
для доступа к атрибуту, например,
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
используйте его для доступа к каждому * атрибуту! Было бы плохой практикой обращаться к некоторым атрибутам, используя @property
и оставлять некоторые другие свойства общедоступными (например, имя без подчеркивания) без доступа, например , не делать
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
Обратите внимание, что self.b
здесь нет явного средства доступа, даже если оно общедоступно.
Точно так же с сеттерами (или мутаторами ), не стесняйтесь использовать, @attribute.setter
но будьте последовательны! Когда вы делаете, например,
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
Мне трудно угадать ваше намерение. С одной стороны, вы говорите, что оба a
и b
являются общедоступными (без подчеркивания в их именах), поэтому мне теоретически должно быть разрешено получить доступ / изменить (получить / установить) оба. Но тогда вы указываете явный мутатор только для a
, который говорит мне, что, возможно, я не должен быть в состоянии установить b
. Поскольку вы предоставили явный мутатор, я не уверен, @property
означает ли отсутствие явного accessor ( ), что я не смогу получить доступ ни к одной из этих переменных, или вы просто экономно использовали @property
.
* Исключение составляют случаи, когда вы явно хотите сделать некоторые переменные доступными или изменяемыми, но не оба, или вы хотите выполнить некоторую дополнительную логику при доступе или изменении атрибута. Это когда я лично использую @property
и @attribute.setter
(в противном случае нет явных acessors / mutators для открытых атрибутов).
Наконец, предложения PEP8 и Google Style Guide:
PEP8, Проектирование для Наследования говорит:
Для простых общедоступных атрибутов данных лучше всего предоставлять только имя атрибута без сложных методов доступа / мутатора . Имейте в виду, что Python обеспечивает легкий путь к будущему усовершенствованию, если вы обнаружите, что простой атрибут данных должен расширять функциональное поведение. В этом случае используйте свойства, чтобы скрыть функциональную реализацию за простым синтаксисом доступа к атрибутам данных.
С другой стороны, в соответствии с Руководством по стилю Google Python Language Rules / Properties рекомендуется:
Используйте свойства в новом коде для доступа к данным или их установки там, где вы обычно использовали бы простые, легкие методы доступа или методы установки. Свойства должны быть созданы с помощью
@property
декоратора.
Плюсы этого подхода:
Читаемость повышается за счет исключения явных вызовов методов get и set для простого доступа к атрибутам. Позволяет вычислениям быть ленивым. Рассматривается Pythonic способ поддерживать интерфейс класса. С точки зрения производительности, разрешение свойств обходится без необходимости тривиальных методов доступа, когда прямой доступ к переменным является разумным. Это также позволяет добавлять методы доступа в будущем без нарушения интерфейса.
и минусы:
Должен наследоваться от
object
Python 2. Может скрывать побочные эффекты, такие как перегрузка операторов. Может быть запутанным для подклассов.
@property
, использование остальных также @property
кажется плохим решением.
@property
(например, выполнение некоторой специальной логики перед возвратом атрибута). В противном случае, почему вы бы украшали один атрибут, @propery
а не другие?
@property
для начала, верно? Если ваш геттер есть, return this._x
а ваш сеттер есть this._x = new_x
, то использовать его @property
вообще глупо.
@property
- это быть последовательным».
Вы можете использовать магические методы __getattribute__
и __setattr__
.
class MyClass:
def __init__(self, attrvalue):
self.myattr = attrvalue
def __getattribute__(self, attr):
if attr == "myattr":
#Getter for myattr
def __setattr__(self, attr):
if attr == "myattr":
#Setter for myattr
Имейте в виду, что __getattr__
и __getattribute__
не то же самое. __getattr__
вызывается только когда атрибут не найден.