Chcę tego zachowania:
class a:
list = []
x = a()
y = a()
x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)
print(x.list) # prints [1, 3]
print(y.list) # prints [2, 4]
Oczywiście to, co naprawdę się dzieje, kiedy drukuję, to:
print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]
Najwyraźniej dzielą się danymi w klasie a
. Jak mogę uzyskać oddzielne wystąpienia, aby osiągnąć pożądane zachowanie?
list
jako nazwy atrybutu.list
jest funkcją wbudowaną do tworzenia nowej listy. Nazwy klas należy pisać dużymi literami.Odpowiedzi:
Chcesz to:
Zadeklarowanie zmiennych wewnątrz deklaracji klasy powoduje, że stają się one członkami „klasy”, a nie elementami instancji. Zadeklarowanie ich w
__init__
metodzie zapewnia, że nowe wystąpienie elementów członkowskich zostanie utworzone wraz z każdym nowym wystąpieniem obiektu, co jest zachowaniem, którego szukasz.źródło
x.list = []
, możesz to zmienić i nie wpływać na inne. Problem, z którym się spotykasz, polega na tym, żex.list
iy.list
są to ta sama lista, więc kiedy wywołujesz dołączenie do jednej, wpływa to na drugą.__init__
.Przyjęta odpowiedź działa, ale trochę więcej wyjaśnień nie boli.
Atrybuty klas nie stają się atrybutami instancji podczas tworzenia instancji. Stają się atrybutami instancji, gdy zostanie im przypisana wartość.
W oryginalnym kodzie żadna wartość nie jest przypisywana do
list
atrybutu po instancji; więc pozostaje atrybutem klasy. Definiowanie listy wewnątrz__init__
działa, ponieważ__init__
jest wywoływana po instancji. Alternatywnie, ten kod dałby również pożądane dane wyjściowe:Jednak zagmatwany scenariusz w pytaniu nigdy nie przydarzy się niezmiennym obiektom, takim jak liczby i łańcuchy, ponieważ ich wartości nie można zmienić bez przypisania. Na przykład kod podobny do oryginału z atrybutem typu string działa bez problemu:
Podsumowując: atrybuty klas stają się atrybutami instancji wtedy i tylko wtedy, gdy wartość jest im przypisana po instancji, będąc w
__init__
metodzie lub nie . Jest to dobra rzecz, ponieważ w ten sposób możesz mieć atrybuty statyczne, jeśli nigdy nie przypiszesz wartości do atrybutu po utworzeniu instancji.źródło
Zadeklarowano „listę” jako „właściwość na poziomie klasy”, a nie jako „właściwość na poziomie instancji”. Aby mieć zakres właściwości na poziomie instancji, należy je zainicjować poprzez odwołanie się do parametru „self” w
__init__
metodzie (lub w innym miejscu, w zależności od sytuacji).Nie musisz ściśle inicjować właściwości instancji w
__init__
metodzie, ale ułatwia to zrozumienie.źródło
Mimo że przyjęta odpowiedź jest trafna, chciałbym nieco dodać opis.
Zróbmy małe ćwiczenie
przede wszystkim zdefiniuj klasę w następujący sposób:
Więc co tu mamy?
temp
będący łańcuchem znaków__init__
Metoda, która ustawiaself.x
self.temp
Jak dotąd całkiem proste, tak? Teraz zacznijmy bawić się tą klasą. Zainicjujmy najpierw tę klasę:
Teraz wykonaj następujące czynności:
Cóż,
a.temp
działało zgodnie z oczekiwaniami, ale jak do diabłaA.temp
działało? Cóż, zadziałało, ponieważ temp jest atrybutem klasy. Wszystko w Pythonie jest obiektem. Tutaj A jest również przedmiotem klasytype
. Zatem atrybut temp jest atrybutem utrzymywanym przezA
klasę i jeśli zmienisz wartość temp za pośrednictwemA
(a nie przez instancjęa
), zmieniona wartość zostanie odzwierciedlona we wszystkich instancjachA
klasy. Zróbmy to dalej:Ciekawe, prawda? I pamiętać, że
id(a.temp)
iid(A.temp)
są wciąż takie same .Każdy obiekt Pythona automatycznie otrzymuje
__dict__
atrybut, który zawiera listę atrybutów. Zbadajmy, co zawiera ten słownik dla naszych przykładowych obiektów:Zauważ, że
temp
atrybut jest wymieniony wśródA
atrybutów klasy, podczas gdyx
jest wymieniony dla instancji.Więc jak to się dzieje, że otrzymujemy określoną wartość,
a.temp
jeśli nie ma jej nawet na liście dla instancjia
. Na tym polega magia__getattribute__()
metody. W Pythonie kropkowana składnia automatycznie wywołuje tę metodę, więc kiedy piszemya.temp
, Python wykonujea.__getattribute__('temp')
. Ta metoda wykonuje akcję wyszukiwania atrybutu, tj. Znajduje wartość atrybutu, patrząc w różnych miejscach.Standardowa implementacja
__getattribute__()
przeszukuje najpierw wewnętrzny słownik ( dict ) obiektu, a następnie typ samego obiektu. W tym przypadkua.__getattribute__('temp')
wykonuje najpierw,a.__dict__['temp']
a potema.__class__.__dict__['temp']
Okej, teraz użyjmy naszej
change
metody:Teraz, gdy już użyliśmy
self
,print(a.temp)
daje nam inną wartość zprint(A.temp)
.Teraz, jeśli porównamy
id(a.temp)
iid(A.temp)
, będą one różne.źródło
Tak, musisz zadeklarować w „konstruktorze”, jeśli chcesz, aby lista stała się właściwością obiektu, a nie właściwością klasy.
źródło
Tak więc prawie każda odpowiedź wydaje się pomijać konkretny punkt. Zmienne klas nigdy nie stają się zmiennymi instancji, jak pokazano w poniższym kodzie. Używając metaklasy do przechwycenia przypisania zmiennej na poziomie klasy, możemy zobaczyć, że po ponownym przypisaniu a.myattr magiczna metoda przypisania pola w klasie nie jest wywoływana. Dzieje się tak, ponieważ przypisanie tworzy nową zmienną instancji . To zachowanie nie ma absolutnie nic wspólnego ze zmienną klasy, jak pokazano w drugiej klasie, która nie ma zmiennych klasowych, a mimo to umożliwia przypisywanie pól.
W SKRÓCIE Zmienne klasowe nie mają NIC wspólnego ze zmiennymi instancji.
Wyraźniej po prostu znajdują się w zakresie wyszukiwania instancji. Zmienne klas są w rzeczywistości zmiennymi instancji w samym obiekcie klasy. Możesz także mieć zmienne metaklasy, jeśli chcesz, ponieważ same metaklasy również są obiektami. Wszystko jest obiektem, niezależnie od tego, czy jest używane do tworzenia innych obiektów, czy nie, więc nie daj się skrępować semantyką użycia słowa class w innych językach. W Pythonie klasa jest po prostu obiektem używanym do określenia, jak tworzyć inne obiekty i jakie będą ich zachowania. Metaklasy to klasy, które tworzą klasy, aby lepiej zilustrować ten punkt.
źródło
Aby chronić zmienną współdzieloną przez inną instancję, musisz tworzyć nową zmienną instancji za każdym razem, gdy tworzysz instancję. Kiedy deklarujesz zmienną wewnątrz klasy, jest to zmienna klasy i jest wspólna dla wszystkich instancji. Jeśli chcesz, aby było to na przykład mądre, musisz użyć metody init , aby ponownie zainicjować zmienną w odniesieniu do instancji
Z obiektów i klas Pythona autorstwa Programiz.com :
Na przykład:
źródło