Я роблю це так:
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
.
Який пітонічний спосіб використовувати геттери та сетери?
"Пітонічний" спосіб полягає не у використанні "гетерів" та "сеттерів", а у використанні простих атрибутів, як це демонструє питання, та 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
але на них посилаються геттер і сетери 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
атрибут від розробників клієнта, а також здійснюєте перевірку типу властивостей імені. Зауважте, що слідуючи такому підходу навіть під час ініціації, сетер викликає. Тому:
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
) означає, що я не маю змоги отримати доступ до жодної з цих змінних, або ви просто нерозумно користуєтесь @property
.
* Виняток становить, коли ви явно хочете зробити деякі змінні доступними або зміненими, але не обидва, або ви хочете виконати додаткову логіку під час доступу до або змінити атрибут. Це коли я особисто використовую @property
і @attribute.setter
(інакше немає явних аксесуарів / мутаторів для публічних атрибутів).
Нарешті, пропозиції PEP8 та Посібник зі стилів Google:
PEP8, Проектування для спадкування, говорить:
Для простих атрибутів загальнодоступних даних найкраще розкрити лише ім’я атрибута без складних методів доступу / мутатора . Майте на увазі, що Python забезпечує простий шлях до майбутнього вдосконалення, якщо ви виявите, що простий атрибут даних потребує зростання функціональної поведінки. У цьому випадку використовуйте властивості, щоб приховати функціональну реалізацію за простим синтаксисом доступу до атрибутів даних.
З іншого боку, згідно з Посібником зі стилів Google, правилами / властивостями Python щодо мов, рекомендується:
Використовуйте властивості в новому коді для доступу чи встановлення даних там, де зазвичай використовувались прості, легкі методи доступу чи налаштування. Властивості слід створити за допомогою
@property
декоратора.
Плюси цього підходу:
Читання збільшується за рахунок виключення явних викликів методу отримання та встановлення для простого доступу до атрибутів. Дозволяє розрахункам бути ледачими. Розглянуто пітонічний спосіб підтримки інтерфейсу класу. З точки зору продуктивності дозволення властивостей обходить необхідні тривіальні методи доступу, коли прямий змінний доступ є розумним. Це також дозволяє в подальшому додавати методи аксесуара, не порушуючи інтерфейс.
і мінуси:
Потрібно успадкувати від
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__
викликається лише тоді, коли атрибут не знайдено.