В чем разница между «свойством» и «атрибутом» Python?

155

Я вообще не понимаю разницы между «свойством» и «атрибутом» и не могу найти отличный ресурс, чтобы кратко описать различия.

Карсон
источник

Ответы:

192

Свойства - это особый вид атрибута. В основном, когда Python встречает следующий код:

spam = SomeObject()
print(spam.eggs)

он смотрит eggsна spam, а затем проверяет , eggsчтобы увидеть , если он имеет __get__, __set__или __delete__метод - если он делает, это свойство. Если это свойство, а не только возвращая eggsобъект (как и для любого другого атрибута) он будет вызывать __get__метод (так как мы делаем поиск) и возвращения независимо этот метод возвращает.

Дополнительная информация о модели данных и дескрипторах Python .

Итан Фурман
источник
Похоже, что свойства требуют дополнительной обработки для доступа к целевому значению ... вы знаете, насколько оно значимо / намного медленнее?
martineau
@martineau: Ну, есть еще один вызов дополнительной функции, но, скорее всего, дополнительная работа и время будут зависеть от того, сколько работает свойство (общий совет: не используйте / не используйте свойства, чтобы скрыть ввод-вывод).
Итан Фурман
61

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

class A(object):
    _x = 0
    '''A._x is an attribute'''

    @property
    def x(self):
        '''
        A.x is a property
        This is the getter method
        '''
        return self._x

    @x.setter
    def x(self, value):
        """
        This is the setter method
        where I can check it's not assigned a value < 0
        """
        if value < 0:
            raise ValueError("Must be >= 0")
        self._x = value

>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
  File "ex.py", line 15, in <module>
    a.x = -1
  File "ex.py", line 9, in x
    raise ValueError("Must be >= 0")
ValueError: Must be >= 0
нейрино
источник
Это («полный контроль») можно сделать и с атрибутами «не-свойства», только без таких простых декораторов.
9
Мне нравится, что этот ответ представляет собой реалистичный и полезный пример. Я считаю, что слишком много ответов на этом сайте излишне объясняют, как все работает на сервере, не разъясняя, как пользователь должен с ними взаимодействовать. Если кто-то не понимает, почему и когда использовать некоторые функции, нет смысла знать, как они работают за кулисами.
Том
Этот ответ нарушает «Дзен Python - должен быть один - и желательно только один - очевидный способ сделать это». Есть 2 способа установить значение x.
Tin
@TinLuu - Есть только один способ установить значение x. Также есть только один способ установить значение _x. То, что это одно и то же, - деталь реализации. Мудрый пользователь этого класса не использует _x.
загорелся
1
@TinLuu - Я думаю, мы оба говорим одно и то же с противоположных сторон. Пользователь класса должен только видеть x. В одну сторону. Если пользователь класса узнает о _x, он использует его на свой страх и риск.
загорелся
20

В общем, свойство и атрибут - это одно и то же. Однако в Python есть декоратор свойств, который обеспечивает доступ для получения / установки атрибута (или других данных).

class MyObject(object):
    # This is a normal attribute
    foo = 1

    @property
    def bar(self):
        return self.foo

    @bar.setter
    def bar(self, value):
        self.foo = value


obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
шесть8
источник
1
не могли бы вы также упомянуть ожидаемый результат этого кода?
Хасан Икбал
2
Что вы имеете в виду? Разве это не внизу кода?
six8 01
13

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

Допустим, например, у вас есть класс, в котором хранятся некоторые координаты x и y для чего-то, что вам нужно. Чтобы установить их, вы можете сделать что-то вроде:

myObj.x = 5
myObj.y = 10

На это гораздо легче смотреть и думать, чем писать:

myObj.setX(5)
myObj.setY(10)

Проблема в том, что, если однажды ваш класс изменится так, что вам нужно будет компенсировать x и y на какое-то значение? Теперь вам нужно будет войти и изменить определение вашего класса и весь код, который его вызывает, что может занять много времени и привести к ошибкам. Свойство позволяет вам использовать первый синтаксис, давая вам гибкость при изменении второго.

В Python вы можете определять методы получения, установки и удаления с помощью функции свойства. Если вам просто нужно свойство read, есть также декоратор @property, который вы можете добавить над своим методом.

http://docs.python.org/library/functions.html#property

Falcojr
источник
Единственный ответ, который имел практический смысл!
Dude156
9

В итоге я узнал 2 отличия от сайта Бернд Кляйн:

1. Свойство - это более удобный способ инкапсуляции данных.

Например, допустим, у вас есть публичный атрибут length. Позже ваш проект требует, чтобы вы инкапсулировали его, то есть изменили его на частный и предоставили геттер и сеттер => вам нужно изменить код, который вы написали ранее:

# Old code
obj1.length = obj1.length + obj2.length
# New code (using private attributes and getter and setter)
obj1.set_length(obj1.get_length() + obj2.get_length()) # => this is ugly

Если вы используете @propertyи @length.setter=>, вам не нужно менять этот старый код.

2. Свойство может содержать несколько атрибутов.

class Person:
  def __init__(self, name, physic_health, mental_health):
    self.name = name
    self.__physic_health = physic_health 
    self.__mental_health = mental_health 

  @property
  def condition(self):
    health = self.__physic_health + self.__mental_health
    if(health < 5.0):
      return "I feel bad!"
    elif health < 8.0:
      return "I am ok!"
    else:
      return "Great!"

В этом примере __physic_healthи __mental_healthявляются частными и не могут быть доступны напрямую извне.

Тин Луу
источник
8

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

class Misc():
        def __init__(self):
            self.test = self.test_func()

        def test_func(self):
            print 'func running'
            return 'func value'

cl = Misc()
print cl.test
print cl.test

Выход:

func running
func value
func value

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

class Misc():

    @property
    def test(self):
        print 'func running'
        return 'func value'

cl = Misc()
print cl.test
print cl.test

Выход:

func running
func value
func running
func value
brc
источник