Пример реального мира о том, как использовать свойство свойства в python?

Мне интересно, как использовать @property в Python. Я прочитал документы 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

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

Спасибо.

Ответ 1

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

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

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

Validation:

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

Ответ 2

Один простой пример использования - установить атрибут экземпляра только для чтения, поскольку, как вам известно, имя переменной с одним символом подчеркивания _x в python обычно означает, что это личное (внутреннее использование), но иногда мы хотим иметь возможность читать а не писать, чтобы мы могли использовать 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

Ответ 3

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

Ответ 4

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

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)

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

Ответ 5

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

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

    return self._x

Ответ 6

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

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

Ответ 7

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

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

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

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

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

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

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

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

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

Ответ 8

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

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')

Ответ 9

Прочитав ответы и комментарии, главной темой, похоже, являются ответы, кажется, что отсутствует простой, но полезный пример. Я включил здесь очень простой пример, демонстрирующий простое использование декоратора @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

Ответ 10

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

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

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

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

bob.age += 1