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?
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
[]
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.
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
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
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.
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
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).
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