Когда и как использовать встроенное свойство функции() в 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.