Jaka jest różnica między atrybutami klasy I instancji?

Czy istnieje jakieś znaczące rozróżnienie między:

class A(object):
    foo = 5   # some default value

Vs.

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

Jeśli tworzysz wiele instancji, czy są jakieś różnice w wydajności lub wymaganiach przestrzeni dla tych dwóch stylów? Czy czytając kod, uważasz znaczenie obu stylów za znacznie odmienne?

Author: martineau, 2008-10-16

6 answers

Poza względami wydajności, istnieje znacząca semantyczna różnica. W przypadku atrybutu klasy jest tylko jeden obiekt, do którego się odnosi. W instance-attribute-set-at-instantiation może być wiele obiektów, do których się odnosi. Na przykład

>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
...  def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo    
[]
 135
Author: Alex Coventry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-16 01:26:53

Różnica polega na tym, że atrybut klasy jest współdzielony przez wszystkie instancje. Atrybut na instancji jest unikalny dla tej instancji.

Jeśli pochodzą z C++, atrybuty klasy są bardziej jak statyczne zmienne członkowskie.

 35
Author: Peter Shinners,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-16 08:16:28

Oto bardzo dobry post i podsumowanie go jak poniżej.

class Bar(object):
    ## No need for dot syntax
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)

## Finds i_var in foo's instance namespace
foo.i_var
## 2

## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1

I w formie wizualnej

Tutaj wpisz opis obrazka

Przypisanie atrybutów klasy

  • Jeśli atrybut klasy zostanie ustawiony przez dostęp do klasy, nadpisze wartość dla wszystkich instancji

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • Jeśli zmienna klasy zostanie ustawiona przez dostęp do instancji, nadpisze ona wartość tylko dla tej instancji . To zasadniczo zastępuje zmienna klasy i zamienia ją w zmienną instancji dostępną intuicyjnie, tylko dla tej instancji .

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

Kiedy użyjesz atrybutu class?

  • Przechowywanie stałych . Ponieważ atrybuty klasy mogą być dostępne jako atrybuty samej klasy, często jest miło używać ich do przechowywania stałych specyficznych dla całej klasy

    class Circle(object):
         pi = 3.14159
    
         def __init__(self, radius):
              self.radius = radius   
        def area(self):
             return Circle.pi * self.radius * self.radius
    
    Circle.pi
    ## 3.14159
    c = Circle(10)
    c.pi
    ## 3.14159
    c.area()
    ## 314.159
    
  • Definiowanie wartości domyślnych . Jako trywialny przykład możemy tworzy listę ograniczoną (np. listę, która może zawierać tylko określoną liczbę elementów lub mniej) i wybiera domyślną wartość 10 elementów

    class MyClass(object):
        limit = 10
    
        def __init__(self):
            self.data = []
        def item(self, i):
            return self.data[i]
    
        def add(self, e):
            if len(self.data) >= self.limit:
                raise Exception("Too many elements")
            self.data.append(e)
    
     MyClass.limit
     ## 10
    
 24
Author: zangw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-07 03:59:08

Ponieważ ludzie w komentarzach i w dwóch innych pytaniach oznaczonych jako dups wydają się być zdezorientowani w tej samej kwestii, myślę, że warto dodać dodatkową odpowiedź na Alexa Coventry ' ego.

Fakt, że Alex przypisuje wartość zmiennego typu, jak lista, nie ma nic wspólnego z tym, czy rzeczy są współdzielone, czy nie. Możemy to zobaczyć za pomocą funkcji id lub operatora is:

>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
...     def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False

(Jeśli zastanawiasz się, dlaczego użyłem object() zamiast, powiedzmy, 5, to jest po to, aby uniknąć dwóch zupełnie innych problemów, których nie chcę tutaj mieszać; z dwóch różnych powodów, całkowicie oddzielnie tworzone 5 s mogą skończyć się tą samą instancją liczby 5. Ale zupełnie oddzielnie tworzone object() s nie mogą.)


Dlaczego więc a.foo.append(5) W przykładzie Alex ma wpływ b.foo, a a.foo = 5 w moim przykładzie nie? Cóż, spróbuj a.foo = 5 W przykładzie Alexa i zauważ, że nie wpływa to na b.foo tam ani .

a.foo = 5 jest po prostu zrobienie a.foo W nazwę dla 5. Nie dotyczy to b.foo, ani żadnej innej nazwy dla starej wartości, do której się odnosiło a.foo.* To trochę trudne, że tworzymy atrybut instancji, który ukrywa atrybut klasy, * * ale kiedy już go uzyskasz, nic skomplikowanego się tutaj nie dzieje.


Mam nadzieję, że teraz jest oczywiste, dlaczego Alex użył listy: fakt, że możesz zmutować listę, oznacza, że łatwiej jest pokazać, że dwie zmienne nazywają tę samą listę, a także oznacza to, że w kodzie rzeczywistym ważniejsze jest, aby wiedzieć, czy masz dwie listy lub dwie nazwy dla tej samej listy.


* zamieszanie dla osób wywodzących się z języka C++ polega na tym, że w Pythonie wartości nie są przechowywane w zmiennych. Wartości żyją w kraju wartości, same w sobie, zmienne są tylko nazwami dla wartości, a przypisanie tworzy nową nazwę dla wartości. Jeśli to pomoże, pomyśl o każdej zmiennej Pythona jako o shared_ptr<T> zamiast o T.

** niektóre ludzie korzystają z tego, używając atrybutu klasy jako "wartości domyślnej" dla atrybutu instancji, który instancje mogą lub nie mogą ustawić. Może to być przydatne w niektórych przypadkach, ale może być również mylące, więc uważaj na to.

 19
Author: abarnert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:07

To tylko opracowanie na temat tego, co powiedział Alex Coventry, inny Alex (Martelli) odniósł się do podobnego pytania na grupie dyskusyjnej comp.lang.python lata temu. Analizuje różnicę semantyczną między tym, co dana osoba zamierzała a tym, co otrzymała (używając zmiennych instancji).

Http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?hl=en

 2
Author: torial,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-15 15:46:06

Jest jeszcze jedna sytuacja.

Atrybutami klasy I instancji jest deskryptor .

# -*- encoding: utf-8 -*-


class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        return self.val


class Base(object):
    attr_1 = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.attr_2 = RevealAccess(10, 'var "x"')


def main():
    b = Base()
    print("Access to class attribute, return: ", Base.attr_1)
    print("Access to instance attribute, return: ", b.attr_2)

if __name__ == '__main__':
    main()

Powyżej wyświetli się:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

Ten sam typ dostępu do instancji poprzez klasę lub instancję zwraca inny wynik!

I znalazłem w c. pyobject_genericgetattr definicja i wielki post.

Wyjaśnij

Jeśli atrybut znajduje się w słowniku klas, które tworzą. obiekty MRO, a następnie sprawdź, czy sprawdzany atrybut wskazuje na deskryptor danych (który nie jest niczym innym, jak klasą implementującą zarówno metody __get__, jak i __set__). Jeśli tak, rozwiąż wyszukiwanie atrybutów przez wywołanie metody __get__ deskryptora danych (linie 28-33).

 0
Author: SimonShyu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-23 10:02:25