Когда и как использовать встроенное свойство функции() в python
Мне кажется, что, за исключением небольшого синтаксического сахара, свойство() ничего не делает.
Конечно, приятно писать a.b=2
вместо a.setB(2)
, но скрывать тот факт, что ab = 2 не является простым назначением, выглядит как рецепт проблемы, либо из-за неожиданного результата, такой как a.b=2
, фактически вызывает a.b
как 1
. Или возникает исключение. Или проблема с производительностью. Или просто запутаться.
Можете ли вы дать мне конкретный пример для его хорошего использования? (использование его для исправления проблемного кода не учитывается; -)
Ответы
Ответ 1
В языках, которые полагаются на геттеры и сеттеры, такие как Java, они не предполагаются и не ожидали ничего, кроме того, что они говорят - было бы удивительно, если бы x.getB()
ничего не делал, кроме как вернуть текущее значение логического атрибута b
, или если x.setB(2)
сделал что-либо, кроме небольшого объема внутренней работы, чтобы сделать x.getB()
return 2
.
Однако нет никаких языковых гарантий относительно этого ожидаемого поведения, т.е. ограничений, связанных с компилятором, с телом методов, имена которых начинаются с get
или set
: скорее, это оставило до здравого смысла, социального конвенция, "руководства по стилю" и тестирование.
Поведение x.b
доступа и назначений, таких как x.b = 2
, на языках, которые имеют свойства (набор языков, включающий, но не ограничиваясь Python), точно такой же, как для методов getter и setter в, например, Java: те же ожидания, те же недостатки гарантированных языками гарантий.
Первый выигрыш для свойств - синтаксис и читаемость. Чтобы написать, например,
x.setB(x.getB() + 1)
вместо очевидного
x.b += 1
кричит о мести богам. В языках, поддерживающих свойства, нет абсолютно никаких оснований для того, чтобы заставить пользователей класса проходить через такие византийские шаблоны, влияя на их читаемость кода без каких-либо проблем.
В Python, в частности, есть еще один большой потенциал для использования свойств (или других дескрипторов) вместо геттеров и сеттеров: если и когда вы реорганизовываете свой класс, чтобы базовый сеттер и получатель больше не нужны, вы можете (без если вы нарушаете класс, опубликованный API), просто устраните те методы и свойство, которое их использует, сделав b
нормальным "сохраненным" атрибутом класса x
, а не "логическим", полученным и установленным вычислительно.
В Python выполнение непосредственно (когда это возможно), а не через методы - важная оптимизация, а систематическое использование свойств позволяет вам выполнять эту оптимизацию, когда это возможно (всегда подвергая "нормальные хранимые атрибуты" напрямую, и только те, которые действительно нужны вычисление при доступе и/или настройке с помощью методов и свойств).
Итак, если вы используете геттеры и сеттеры вместо свойств, не влияя на читаемость кода ваших пользователей, вы также безвозмездно тратите машинные циклы (и энергию, которая поступает на их компьютер в течение этих циклов;-), снова без уважительной причины.
Ваш единственный аргумент против свойств - это, например, что "внешний пользователь не будет ожидать каких-либо побочных эффектов в результате назначения, как правило"; но вы упускаете из виду тот факт, что один и тот же пользователь (на языке Java, где геттеры и сеттеры повсеместно) не ожидает (наблюдаемых) "побочных эффектов" в результате вызова сеттера (или даже меньше для геттера;-). Они разумные ожидания, и вам, как автору класса, пытаться их уместить - независимо от того, используются ли ваши сеттер и геттер напрямую или через свойство, не имеет значения. Если у вас есть методы с важными наблюдаемыми побочными эффектами, не назовите их getThis
, setThat
и не используйте их через свойства.
Жалоба на то, что свойства "скрыть реализацию" полностью необоснованна: большинство OOP - это реализация скрытия информации - создание класса, ответственного за представление логического интерфейса для внешнего мира и его реализацию как можно лучше. Геттеры и сеттеры, как и свойства, являются инструментами для достижения этой цели. Свойства просто улучшают работу (на языках, которые их поддерживают;).
Ответ 2
Идея состоит в том, чтобы позволить вам избегать писать геттеры и сеттеры, пока вы им не понадобятся.
Итак, для начала вы пишете:
class MyClass(object):
def __init__(self):
self.myval = 4
Очевидно, теперь вы можете написать myobj.myval = 5
.
Но позже вы решаете, что вам нужен сеттер, так как вы хотите сделать что-то умное одновременно. Но вы не хотите менять весь код, который использует ваш класс, - поэтому вы обертываете сеттер в декораторе @property
, и все это просто работает.
Ответ 3
но скрывая тот факт, что a.b = 2 не является простое назначение выглядит как рецепт для проблемы
Ты не скрываешь этого факта; этого факта никогда не было. Это python - язык высокого уровня; не сборка. Немногие из "простых" утверждений в нем сводятся к отдельным инструкциям CPU. Чтобы прочитать простоту в задании, нужно читать вещи, которых там нет.
Когда вы говорите x.b = c, возможно, все, что вам нужно подумать, это то, что "все, что только что произошло, x.b теперь должно быть c".
Ответ 4
Основная причина в том, что она выглядит лучше. Это больше питонов. Особенно для библиотек. something.getValue() выглядит менее приятным, чем something.value
В plone (довольно большой CMS) у вас был документ document.setTitle(), который выполняет множество функций, таких как сохранение значения, его индексация и т.д. Просто делать document.title= "что-то" лучше. Вы знаете, что многое все-таки происходит за кулисами.
Ответ 5
Вы правы, это просто синтаксический сахар. Возможно, в этом нет хорошего использования в зависимости от вашего определения проблемного кода.
Учтите, что у вас есть класс Foo, который широко используется в вашем приложении. Теперь это приложение получило достаточно большой и, кроме того, позволяет сказать, что он стал популярным.
Вы определяете, что Foo вызывает узкое место. Возможно, можно добавить некоторое кеширование в Foo, чтобы ускорить его. Использование свойств позволит вам это сделать без изменения кода или тестов вне Foo.
Да, конечно, это проблематичный код, но вы просто сохранили много $$, исправляя его быстро.
Что делать, если Foo находится в библиотеке, для которой есть сотни или тысячи пользователей? Ну, вы спасли себя, чтобы сказать им сделать дорогостоящий рефактор, когда они перейдут на новую версию Foo.
В примечаниях к выпуску есть линейный элемент о Foo вместо руководства по переносу абзаца.
Опытные программисты на Python не ожидают большего от a.b=2
, кроме a.b==2
, но они знают, что это может быть неверно. Что происходит внутри класса - это собственный бизнес.
Ответ 6
Вот старый пример. Я завернул библиотеку C, которая имела такие функции, как "void dt_setcharge (int atom_handle, int new_charge)" и "int dt_getcharge (int atom_handle)". Я хотел на уровне Python выполнить "atom.charge = atom.charge + 1".
Декоратор "property" делает это легко. Что-то вроде:
class Atom(object):
def __init__(self, handle):
self.handle = handle
def _get_charge(self):
return dt_getcharge(self.handle)
def _set_charge(self, charge):
dt_setcharge(self.handle, charge)
charge = property(_get_charge, _set_charge)
10 лет назад, когда я написал этот пакет, мне пришлось использовать __getattr__ и __setattr__, которые сделали это возможным, но реализация была гораздо более подверженной ошибкам.
class Atom:
def __init__(self, handle):
self.handle = handle
def __getattr__(self, name):
if name == "charge":
return dt_getcharge(self.handle)
raise AttributeError(name)
def __setattr__(self, name, value):
if name == "charge":
dt_setcharge(self.handle, value)
else:
self.__dict__[name] = value
Ответ 7
геттеры и сеттеры необходимы для многих целей и очень полезны, потому что они прозрачны для кода. Имея объект Что-то высоту свойства, вы присваиваете значение Something.height = 10, но если высота имеет геттер и сеттер, то в то время, когда вы назначаете это значение, вы можете делать много вещей в процедурах, например, для проверки минимального или максимального значения значение, например, запуск события, потому что высота изменилась, автоматически устанавливая другие значения в функции нового значения высоты, все, что может произойти в момент, когда было присвоено значение Something.height. Помните, что вам не нужно вызывать их в своем коде, они автоматически выполняются в момент, когда вы читаете или записываете значение свойства. В некотором роде они похожи на процедуры событий, когда свойство X изменяет значение и когда считывается значение свойства X.