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 jest jakaś różnica w wydajności lub wymaganej przestrzeni dla obu stylów? Czy czytając kod, uważasz, że znaczenie tych dwóch stylów znacząco się różni?
python
attributes
member-variables
Dan Homerick
źródło
źródło
Odpowiedzi:
Poza względami wydajnościowymi istnieje znacząca różnica semantyczna . W przypadku atrybutu klasy istnieje tylko odniesienie do jednego obiektu. W instancji atrybutu zestaw w instancji może istnieć wiele obiektów, do których można się odwołać. 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 []
źródło
int
istr
nadal są związani z każdą instancją, a nie klasą.int
istr
są również udostępniane, dokładnie w ten sam sposób. Możesz to łatwo sprawdzić za pomocąis
lubid
. Lub po prostu spójrz na każdą instancję__dict__
i klasę__dict__
. Zwykle nie ma większego znaczenia, czy niezmienne typy są udostępniane, czy nie.a.foo = 5
, w obu przypadkach zobaczyszb.foo
powrót[]
. Dzieje się tak, ponieważ w pierwszym przypadku nadpisujesz atrybut klasya.foo
nowym atrybutem instancji o tej samej nazwie.Różnica polega na tym, że atrybut w klasie jest wspólny dla wszystkich instancji. Atrybut instancji jest unikatowy dla tej instancji.
Jeśli pochodzą z C ++, atrybuty w klasie są bardziej podobne do statycznych zmiennych składowych.
źródło
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
mutuje wartość,a.foo
do której się odwołuje,a.foo = 5
tworzya.foo
nową nazwę wartości5
. Otrzymujesz więc atrybut instancji, który ukrywa atrybut klasy. Spróbuj tego samegoa.foo = 5
w wersji Alexa, a zobaczysz, żeb.foo
to się nie zmieniło.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
Przypisanie atrybutu 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 jest ustawiana poprzez dostęp do instancji, nadpisuje wartość tylko dla tej instancji . To zasadniczo przesłania zmienną 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żyłbyś atrybutu class?
Przechowywanie stałych . Ponieważ atrybuty klas mogą być dostępne jako atrybuty samej klasy, często dobrze jest ich używać do przechowywania stałych dla całej klasy, specyficznych dla 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 utworzyć ograniczoną listę (tj. Listę, która może zawierać tylko określoną liczbę elementów lub mniej) i wybrać domyślny limit 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
źródło
Ponieważ ludzie w komentarzach tutaj iw dwóch innych pytaniach oznaczonych jako dupki wydają się być zdezorientowani w ten sam sposób, myślę, że warto dodać dodatkową odpowiedź oprócz odpowiedzi Alexa Coventry'ego .
Fakt, że Alex przypisuje wartość typu zmiennego, takiego jak lista, nie ma nic wspólnego z tym, czy rzeczy są udostępniane, czy nie. Widzimy to za pomocą
id
funkcji lubis
operatora:>>> 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
aby uniknąć napotkania dwóch innych problemów, których nie chcę tutaj omawiać; z dwóch różnych powodów, całkowicie oddzielnie utworzone5
pliki mogą ostatecznie stać się to samo wystąpienie liczby5
. Ale całkowicie oddzielnie utworzoneobject()
s nie mogą).Dlaczego więc
a.foo.append(5)
w przykładzie Alexa ma to wpływb.foo
, aa.foo = 5
w moim nie? Dobrze, spróbuja.foo = 5
na przykład Alexa, i zauważ, że nie wpływa onb.foo
tam albo .a.foo = 5
po prostu tworzya.foo
nazwę dla5
. To nie ma wpływub.foo
, ani żadna inna nazwa starej wartości,a.foo
do której się odnosiliśmy. * To trochę skomplikowane, że tworzymy atrybut instancji, który ukrywa atrybut klasy, ** ale kiedy już to zdobędziesz, nic skomplikowanego nie jest dzieje się tutaj.Mamy 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, że w prawdziwym kodzie ważniejsze jest, aby wiedzieć, czy masz dwie listy, czy dwie nazwy dla tej samej listy.
* Zamieszanie dla ludzi wywodzących się z języka takiego jak C ++ polega na tym, że w Pythonie wartości nie są przechowywane w zmiennych. Wartości same w sobie żyją na gruncie wartości, zmienne są tylko nazwami wartości, a przypisanie tworzy po prostu nową nazwę wartości. Jeśli to pomoże, pomyśl o każdej zmiennej Pythona jako o
shared_ptr<T>
zamiast aT
.** Niektóre osoby wykorzystują to, używając atrybutu class jako „wartości domyślnej” atrybutu instancji, który instancje mogą lub nie mogą być ustawiane. Może to być przydatne w niektórych przypadkach, ale może też być mylące, więc bądź ostrożny.
źródło
Jest jeszcze jedna sytuacja.
Atrybuty klasy i instancji to Descriptor .
# -*- 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ższe wyświetli:
('Access to class attribute, return: ', 10) ('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)
Ten sam typ dostępu do instancji za pośrednictwem klasy lub instancji zwraca inny wynik!
I znalazłem w definicji c.PyObject_GenericGetAttr, i świetny post .
Wyjaśnić
źródło