Пример из реального мира о том, как использовать свойство свойства в Python? - PullRequest
131 голосов
/ 10 июня 2011

Меня интересует, как использовать @property в Python.Я прочитал документы по питону, и пример, на мой взгляд, просто игрушечный код:

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

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

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Я не знаю, какие преимущества я могу получить от упаковки _xс имущественным декоратором.Почему бы просто не реализовать как:

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

Я думаю, свойство свойства может быть полезно в некоторых ситуациях.Но когда?Может кто-нибудь дать мне примеры из реальной жизни?

Спасибо.

Ответы [ 10 ]

87 голосов
/ 10 июня 2011

Другими примерами могут быть проверка / фильтрация установленных атрибутов (заставляя их быть в границах или приемлемыми) и ленивая оценка сложных или быстро меняющихся терминов.

Сложный расчет, скрытый за атрибутом:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

Проверка:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value
74 голосов
/ 10 июня 2011

Одним из простых вариантов использования будет установка атрибута экземпляра только для чтения, поскольку вы знаете, что ведение имени переменной с одним подчеркиванием _x в python обычно означает, что это private (внутреннее использование), но иногда мычтобы иметь возможность читать атрибут экземпляра и не записывать его, поэтому мы можем использовать property для этого:

>>> class C(object):

        def __init__(self, x):
            self._x = x

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute
20 голосов
/ 10 июня 2011

Взгляните на эту статью для очень практического использования. Вкратце, это объясняет, как в Python вы обычно можете отказаться от явного метода getter / setter, так как если вам они понадобятся на каком-то этапе, вы можете использовать property для бесшовной реализации.

15 голосов
/ 10 июня 2011

Я использовал его для кэширования медленных, но неизменяемых значений, хранящихся в базе данных.Это обобщает любую ситуацию, когда ваши атрибуты требуют вычислений или какой-либо другой длительной операции (например, проверка базы данных, сетевое взаимодействие), которую вы хотите выполнять только по требованию.

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

Это было в веб-приложении, где любойданному представлению страницы может понадобиться только один конкретный атрибут такого рода, но сами базовые объекты могут иметь несколько таких атрибутов - инициализация их всех при создании будет расточительной, а свойства позволяют мне быть гибкими, когда атрибуты ленивы, а какие нет..

7 голосов
/ 10 июня 2011

Свойство - это просто абстракция вокруг поля, которая дает вам больший контроль над способами управления конкретным полем и вычислениями промежуточного программного обеспечения. Мало что приходит на ум - проверка, предварительная инициализация и ограничение доступа

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x
6 голосов
/ 17 февраля 2017

Прочитав ответы и комментарии, основная тема, кажется, состоит в том, что ответы, кажется, упускают простой, но полезный пример.Я включил очень простой пример, демонстрирующий простое использование декоратора @property.Это класс, который позволяет пользователю задавать и получать измерения расстояния, используя различные единицы измерения, например in_feet или in_metres.

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

Использование:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14
6 голосов
/ 27 июня 2016

еще одна приятная особенность свойств, связанная с использованием сеттеров и геттеров, это то, что они позволяют вам продолжать использовать операторы OP = (например, + =, - =, * = и т. Д.) На вашем атрибуты, сохраняя при этом любую проверку, управление доступом, кэширование и т. д., которые установщики и получатели могли бы предоставить.

например, если вы написали класс Person с установщиком setage(newage) и получателем getage(), то для увеличения возраста вам нужно будет написать:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

но если вы сделали age свойством, вы могли бы написать намного чище:

bob.age += 1
5 голосов
/ 01 января 2016

Да, для исходного размещенного примера свойство будет работать точно так же, как просто наличие переменной экземпляра 'x'.

Это лучшее, что есть в свойствах python.Снаружи они работают точно так же, как переменные экземпляра!Что позволяет вам использовать переменные экземпляра извне класса.

Это означает, что ваш первый пример может фактически использовать переменную экземпляра.Если что-то изменилось, и тогда вы решите изменить свою реализацию, и свойство будет полезным, интерфейс со свойством будет таким же, как и в коде вне класса. Изменение переменной экземпляра на свойство не влияет на код вне класса.

Многие другие языки и курсы программирования будут указывать, что программист никогда не должен выставлять переменные экземпляра, а вместо этого использовать 'getters'и' setters 'для любого значения, к которому нужно получить доступ из-за пределов класса, даже в простом случае, указанном в вопросе.

Код вне класса со многими языками (например, Java) использует

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

И при реализации класса есть много «получателей» и «установщиков», которые в точности соответствуют первому примеру: реплицируют просто переменную экземпляра.Эти методы получения и установки требуются, потому что, если реализация класса изменится, весь код за пределами класса должен будет измениться.Но свойства python позволяют коду вне класса быть таким же, как с переменными экземпляра.Поэтому нет необходимости изменять код вне класса, если вы добавляете свойство или имеете простую переменную экземпляра.Таким образом, в отличие от большинства объектно-ориентированных языков, для вашего простого примера вы можете использовать переменную экземпляра вместо «getters» и «setters», которые на самом деле не нужны, убедитесь, что еслив будущем вы переходите к свойству, код, использующий ваш класс, изменять не нужно.

Это означает, что вам нужно создавать свойства только в случае сложного поведения, и для очень простого простого случая, где, как описано ввопрос, простая переменная экземпляра - это все, что нужно, вы можете просто использовать переменную экземпляра.

5 голосов
/ 15 июля 2014

Короткий ответ на ваш вопрос заключается в том, что в вашем примере нет никакой выгоды. Вам, вероятно, следует использовать форму, которая не содержит свойств.

Причина, по которой свойства существуют, заключается в том, что если ваш код изменится в будущем, и вам вдруг понадобится сделать больше с вашими данными: кэшировать значения, защищать доступ, запрашивать некоторый внешний ресурс ... что угодно, вы можете легко изменить свой класс добавить геттеры и сеттеры для данных без изменения интерфейса, чтобы вам не приходилось искать в коде всюду доступ к этим данным и изменять его тоже.

4 голосов
/ 10 января 2014

Поначалу многие не замечают, что вы можете создавать собственные подклассы собственности. Это я нашел очень полезным для предоставления атрибутов объекта только для чтения или атрибута, который вы можете читать и писать, но не удалять. Это также отличный способ обернуть такую ​​функциональность, как отслеживание изменений в полях объекта.

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...