Jaka jest różnica między „właściwością” a „atrybutem” w Pythonie?

146

Ogólnie jestem zdezorientowany różnicą między „właściwością” a „atrybutem” i nie mogę znaleźć dobrego źródła, aby zwięźle opisać różnice.

Carson
źródło

Odpowiedzi:

184

Właściwości to specjalny rodzaj atrybutów. Zasadniczo, gdy Python napotka następujący kod:

spam = SomeObject()
print(spam.eggs)

wygląda się eggsw spam, a następnie sprawdza eggs, czy to ma __get__, __set__albo __delete__metodę - jeśli to robi, to jest nieruchomość. Jeśli jest to właściwość, zamiast po prostu zwracać eggsobiekt (tak jak w przypadku każdego innego atrybutu), wywoła __get__metodę (ponieważ szukaliśmy) i zwróci cokolwiek zwraca ta metoda.

Więcej informacji o modelu danych i deskryptorach Pythona .

Ethan Furman
źródło
Wygląda na to, że właściwości wymagają dodatkowego przetwarzania w celu uzyskania dostępu do wartości docelowej ... czy wiesz, jak istotne / dużo wolniejsze jest to?
martineau
@martineau: Cóż, jest jedno dodatkowe wywołanie funkcji, ale najprawdopodobniej dodatkowa praca i czas będą zależeć od tego, ile robi właściwość (porada ogólna: nie używaj / nie używaj właściwości do ukrywania operacji we / wy).
Ethan Furman,
56

Dzięki właściwości masz pełną kontrolę nad jej metodami pobierającymi, ustawiającymi i usuwającymi, których nie masz (jeśli nie używasz zastrzeżeń) z atrybutem.

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
neurino
źródło
Ta („pełna kontrola”) może być również wykonana przy użyciu atrybutów „nie-własności”, tylko bez takich prostych dekoratorów.
8
Podoba mi się, że ta odpowiedź stanowi realistyczny i użyteczny przykład. Uważam, że zbyt wiele odpowiedzi na tej stronie niepotrzebnie wyjaśnia, jak działają rzeczy na zapleczu, bez wyjaśniania, jak użytkownik powinien z nimi współdziałać. Jeśli ktoś nie rozumie, dlaczego / kiedy użyć jakiejś funkcjonalności, nie ma sensu wiedzieć, jak działa ona za kulisami.
Tom
Ta odpowiedź narusza zasadę „Zen Pythona - powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego”. Istnieją 2 sposoby ustawienia wartości x.
Tin Luu
@TinLuu - Jest tylko jeden sposób na ustawienie wartości x. Jest też tylko jeden sposób ustawienia wartości _x. Fakt, że są tym samym, jest szczegółem implementacji. Mądry użytkownik tej klasy nie używa _x.
zapalił się
1
@TinLuu - Myślę, że obaj mówimy to samo z przeciwnych końców perspektywy. Użytkownik klasy powinien tylko widzieć x. Jednokierunkowa. Jeśli użytkownik klasy dowie się o _x, użyje go na własne ryzyko.
zapalił
19

Ogólnie rzecz biorąc, właściwość i atrybut to to samo. Jednak w Pythonie istnieje dekorator właściwości, który zapewnia pobierający / ustawiający dostęp do atrybutu (lub innych danych).

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
sześć8
źródło
1
czy mógłbyś również wspomnieć o oczekiwanym wyniku tego kodu?
Hasan Iqbal
2
Co masz na myśli? Czy to nie jest na dole kodu?
szósty 8
12

Ta właściwość pozwala na pobieranie i ustawianie wartości tak, jak w przypadku zwykłych atrybutów, ale pod spodem znajduje się metoda nazywana tłumaczeniem jej na metodę pobierającą i ustawiającą. To naprawdę tylko wygoda, aby ograniczyć schemat wywoływania metod pobierających i ustawiających.

Powiedzmy na przykład, że masz klasę, która zawierała jakieś współrzędne xiy dla czegoś, czego potrzebujesz. Aby je ustawić, możesz zrobić coś takiego:

myObj.x = 5
myObj.y = 10

O wiele łatwiej jest na to spojrzeć i pomyśleć niż napisać:

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

Problem w tym, co się stanie, jeśli pewnego dnia twoja klasa zmieni się tak, że będziesz musiał zrównoważyć swoje x i y o jakąś wartość? Teraz musisz wejść i zmienić definicję klasy i cały kod, który ją wywołuje, co może być naprawdę czasochłonne i podatne na błędy. Właściwość pozwala na użycie poprzedniej składni, dając jednocześnie elastyczność zmiany drugiej.

W Pythonie można definiować metody pobierające, ustawiające i usuwające za pomocą funkcji właściwości. Jeśli potrzebujesz tylko właściwości read, istnieje również dekorator @property, który możesz dodać nad metodą.

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

falcojr
źródło
Tylko odpowiedź, która miała praktyczny sens!
Dude156
8

Dowiedziałem się 2 różnic ze strony Bernda Kleina, podsumowując:

1. Właściwość jest wygodniejszym sposobem enkapsulacji danych.

np. jeśli masz atrybut public długość Object, później projekt wymaga, abyś go hermetyzował, tj .: zmień go na prywatny i podaj getter i setter => musisz zmienić wiele kodów, które napisałeś wcześniej:

#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

Jeśli używasz @property i @ lenght.setter =>, nie musisz zmieniać tych starych kodów

2. Właściwość może zawierać wiele atrybutów

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!"

W tym przykładzie __physic_healthi __mental_healthsą prywatne i nie można uzyskać do nich dostępu bezpośrednio z zewnątrz, jedynym sposobem interakcji z nimi poza klasą jest poprzez właściwośćcondition

Tin Luu
źródło
8

Jest też jedna nieoczywista różnica, której używam do buforowania lub odświeżania danych, często mamy funkcję połączoną z atrybutem klasy. Na przykład muszę raz przeczytać plik i zachować zawartość przypisaną do atrybutu, aby wartość była buforowana:

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

Wynik:

func running
func value
func value

Uzyskaliśmy dostęp do atrybutu dwukrotnie, ale nasza funkcja została uruchomiona tylko raz. Zmiana powyższego przykładu na użycie właściwości spowoduje odświeżenie wartości atrybutu przy każdym dostępie do niego:

class Misc():

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

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

Wynik:

func running
func value
func running
func value
brc
źródło