Меня интересует, как использовать @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
Думаю, в некоторых ситуациях функция свойств может пригодиться. Но когда? Не мог бы кто-нибудь дать мне несколько реальных примеров?
Спасибо.
Ответы:
Другими примерами могут быть проверка / фильтрация установленных атрибутов (принуждение их к ограничению или приемлемости) и ленивая оценка сложных или быстро меняющихся терминов.
Сложный расчет, скрытый за атрибутом:
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
источник
Одним из простых вариантов использования будет установка атрибута экземпляра только для чтения, поскольку вы знаете, что начало имени переменной с одним подчеркиванием
_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
источник
c._x
, если пользователь хочет. Python на самом деле не имеет настоящих частных атрибутов.Взгляните на эту статью для очень практического использования. Короче говоря, он объясняет, как в Python вы обычно можете отказаться от явного метода получения / установки, поскольку, если они вам понадобятся на каком-то этапе, вы можете использовать их
property
для бесшовной реализации.источник
Одна вещь, для которой я его использовал, - это кеширование медленно просматриваемых, но неизменных значений, хранящихся в базе данных. Это распространяется на любую ситуацию, когда ваши атрибуты требуют вычисления или какой-либо другой длительной операции (например, проверка базы данных, сетевое взаимодействие), которую вы хотите выполнять только по запросу.
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)
Это было в веб-приложении, где любому заданному просмотру страницы мог потребоваться только один конкретный атрибут такого типа, но сами базовые объекты могли иметь несколько таких атрибутов - инициализация их всех при построении была бы расточительной, а свойства позволяют мне быть гибкими в том, что атрибуты ленивы, а которые нет.
источник
@cached_property
для этого?Читая ответы и комментарии, кажется, что основная тема заключается в том, что в ответах не хватает простого, но полезного примера. Я включил сюда очень простой пример, демонстрирующий простое использование
@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
источник
Свойство - это просто абстракция вокруг поля, которая дает вам больше контроля над способами манипулирования определенным полем и выполнения вычислений промежуточного программного обеспечения. На ум приходят лишь некоторые примеры использования: проверка, предварительная инициализация и ограничение доступа.
@property def x(self): """I'm the 'x' property.""" if self._x is None: self._x = Foo() return self._x
источник
Да, в исходном опубликованном примере свойство будет работать точно так же, как просто наличие переменной экземпляра 'x'.
Это лучшее, что есть в свойствах Python. С внешней стороны они работают точно так же, как переменные экземпляра! Это позволяет вам использовать переменные экземпляра извне класса.
Это означает, что ваш первый пример может действительно использовать переменную экземпляра. Если что-то изменилось, а затем вы решите изменить свою реализацию и свойство окажется полезным, интерфейс к свойству останется таким же, как из кода вне класса. Переход от переменной экземпляра к свойству не влияет на код вне класса.
Многие другие языки и курсы программирования будут инструктировать, что программист никогда не должен раскрывать переменные экземпляра, а вместо этого использовать «геттеры» и «сеттеры» для любого значения, к которому будет осуществляться доступ извне класса, даже в простом случае, указанном в вопросе.
Код вне класса с использованием многих языков (например, Java)
object.get_i() #and object.set_i(value) #in place of (with python) object.i #and object.i = value
И при реализации класса есть много «геттеров» и «сеттеров», которые действуют точно так же, как ваш первый пример: реплицируют просто переменную экземпляра. Эти геттеры и сеттеры необходимы, потому что, если реализация класса изменится, весь код вне класса должен будет измениться. Но свойства python позволяют коду вне класса быть таким же, как с переменными экземпляра. Таким образом, код вне класса не нужно изменять, если вы добавляете свойство или имеете простую переменную экземпляра. Итак, в отличие от большинства объектно-ориентированных языков, для вашего простого примера вы можете использовать переменную экземпляра вместо «геттеров» и «сеттеров», которые на самом деле не нужны, будучи уверенными в том, что если вы измените свойство в будущем, код, использующий ваш класс не нужно менять.
Это означает, что вам нужно создавать свойства только в том случае, если существует сложное поведение, а для очень распространенного простого случая, когда, как описано в вопросе, все, что требуется, это простая переменная экземпляра, вы можете просто использовать переменную экземпляра.
источник
еще одна приятная особенность свойств по сравнению с использованием сеттеров и получателей, это то, что они позволяют вам продолжать использовать операторы OP = (например, + =, - =, * = и т.д.) в ваших атрибутах, сохраняя при этом любые проверки, контроль доступа, кеширование и т.д., которые сеттеры и геттеры предоставят.
например, если вы написали класс
Person
с сеттеромsetage(newage)
и геттеромgetage()
, то для увеличения возраста вам нужно будет написать:bob = Person('Robert', 25) bob.setage(bob.getage() + 1)
но если вы создали
age
свойство, вы могли бы написать намного чище:bob.age += 1
источник
Короткий ответ на ваш вопрос заключается в том, что в вашем примере нет никакой выгоды. Вероятно, вам следует использовать форму, в которой нет свойств.
Причина существования свойств заключается в том, что если ваш код изменится в будущем, и вам внезапно потребуется что-то большее с вашими данными: значения кеширования, защита доступа, запрос какого-либо внешнего ресурса ... в любом случае, вы можете легко изменить свой класс, чтобы добавить геттеры и сеттеры для данных без изменения интерфейса, поэтому вам не нужно везде в коде находить доступ к этим данным и менять их тоже.
источник
То, что многие сначала не замечают, - это то, что вы можете создавать свои собственные подклассы свойств. Я нашел это очень полезным для отображения атрибутов объекта только для чтения или атрибутов, которые вы можете читать и писать, но не можете удалить. Это также отличный способ обернуть такие функции, как отслеживание изменений в полях объекта.
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')
источник
attr = reader('_attr')
строку или какую-то форму предварительной проверки, напримерattr = if self.__isValid(value): reader('_attr')
. Предложения?attr = reader('_attr')
наattr = accessor('_attr')
. Спасибо