Яка різниця між властивістю Python та "атрибутом"?


146

Я взагалі плутаю різницю між "властивістю" та "атрибутом", і не можу знайти чудовий ресурс, щоб чітко деталізувати відмінності.

Відповіді:


184

Властивості - особливий вид атрибута. В основному, коли Python стикається з таким кодом:

spam = SomeObject()
print(spam.eggs)

він дивиться eggsна spam, а потім перевіряє , eggsщоб побачити , якщо він має __get__, __set__або __delete__метод - якщо він робить, це властивість. Якщо це властивість, а НЕ тільки повертаючи eggsоб'єкт (як і для будь-якого іншого атрибута) він буде викликати __get__метод (так як ми робимо пошук) і повернення незалежно цей метод повертає.

Більше інформації про модель даних та дескриптори Python .


Здається, властивості передбачають додаткову обробку для доступу до цільового значення ... чи знаєте ви, наскільки це значно / набагато повільніше?
мартіно

@martineau: Ну, є один додатковий виклик функції, але, швидше за все, додаткова робота та час залежатимуть від того, наскільки власність виконує (загальна порада: робити / не / використовувати властивості, щоб приховати введення-виведення).
Етан Фурман

56

З властивістю ви маєте повний контроль над його методами getter, setter та Deleter, яких у вас немає (якщо ви не використовуєте застереження) з атрибутом.

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

Це ("повний контроль") можна виконати і з атрибутами "невластивості", тільки без таких простих декораторів.

8
Мені подобається, що ця відповідь дає реалістичний та корисний приклад. Я вважаю, що занадто багато відповідей на цьому веб-сайті непотрібно пояснювати, як все працює на задньому плані, не пояснюючи, як користувач повинен взаємодіяти з ними. Якщо хтось не розуміє, чому / коли використовувати якийсь функціонал, немає сенсу знати, як він працює за кадром.
Том

Ця відповідь порушує "Дзен Пітона - Має бути один - і бажано лише один - очевидний спосіб зробити це". Є два способи встановити значення x.
Tin Luu

@TinLuu - Існує лише один спосіб встановити значення x. Існує також лише один спосіб встановити значення _x. Те, що вони однакові, - це деталь реалізації. Мудрий користувач цього класу не використовує _x.
запалили

1
@TinLuu - Я думаю, що ми обидва говоримо одне й те саме з протилежних кінців точки зору. Користувач класу повинен бачити лише x. Односторонній. Якщо користувач класу дізнається про _x, він використовує його на власний ризик.
запалено

19

Взагалі кажучи, властивість та атрибут - це одне і те ж. Однак в 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

1
Ви можете також зазначити очікуваний результат цього коду?
Хасан Ікбал

2
Що ви маєте на увазі? Чи не це внизу коду?
шість8

12

Властивість дозволяє отримувати та встановлювати значення на зразок звичайних атрибутів, але під ними є метод, який називається переведенням його в getter та setter. Насправді просто зручно скоротити котлован для виклику геттерів та сетерів.

Скажімо, наприклад, у вас був клас, який містив деякі координати x і y для чогось, що вам потрібно. Щоб встановити їх, ви можете зробити щось на кшталт:

myObj.x = 5
myObj.y = 10

Це набагато простіше подивитися і подумати, ніж написати:

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

Проблема полягає в тому, що, якщо одного дня ваш клас зміниться таким чином, що вам потрібно компенсувати свої значення x і y деяким значенням? Тепер вам потрібно буде зайти і змінити своє визначення класу та весь код, який його викликає, що може зайняти багато часу та схильність до помилок. Властивість дозволяє використовувати колишній синтаксис, надаючи вам гнучкість зміни останнього.

У Python ви можете визначити getters, setters та видалити методи за допомогою функції властивостей. Якщо ви просто хочете властивість прочитати, є також декоратор @property, який ви можете додати вище свого методу.

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


Лише відповідь, яка мала практичний сенс!
Чувак156,

8

Я дізнався 2 відмінності від сайту Бернда Кляйна, підсумовуючи:

1. Властивість - це більш зручний спосіб зробити інкапсуляцію даних.

Напр .: Якщо у вас є загальнодоступна довжина атрибуту Object, пізніше ваш проект вимагає від вас інкапсуляції, тобто: змінити його на приватну та надати getter і setter => ви повинні змінити багато кодів, які ви писали раніше:

#Old codes
obj1.length=obj1.length+obj2.length
#New codes(Using private attibutes and getter and setter)
obj1.set_lenght(obj1.get_length()+obj2.get_length()) #=> this is ugly

Якщо ви використовуєте @property та @ lenght.setter => вам не потрібно змінювати ці старі коди

2. Властивість може інкапсулювати кілька атрибутів

class Person:
  def __init__(self, name, physic_health, mental_health):
    self.name=name
    self.__physic_health=physic_health #physic_health is real value in range [0, 5.0]
    self.__mental_health=mental_health #mental_health is real value in range [0, 5.0]
  @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є приватними і не може бути доступна безпосередньо з поза боку, єдиного способу за межі клас взаємодіє з ними Повсюдно нерухомістюcondition


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.