Dobra, najpierw najważniejsze.
W Pythonie nie ma czegoś takiego jak „deklaracja zmiennej” czy „inicjalizacja zmiennej”.
Istnieje po prostu coś, co nazywamy „przypisaniem”, ale prawdopodobnie powinno to być po prostu nazwane „nazewnictwem”.
Przypisanie oznacza „ta nazwa po lewej stronie odnosi się teraz do wyniku oceny prawej strony, niezależnie od tego, do czego się odnosiło wcześniej (jeśli cokolwiek)”.
foo = 'bar'
foo = 2 * 3
Jako takie, nazwy Pythona (prawdopodobnie lepsze określenie niż „zmienne”) nie mają skojarzonych typów; wartości tak. Możesz ponownie zastosować tę samą nazwę do wszystkiego, niezależnie od jego typu, ale rzecz nadal zachowuje się zależnie od jej typu. Nazwa jest po prostu sposobem na odwołanie się do wartości (obiektu). To odpowiada na twoje drugie pytanie: nie tworzysz zmiennych do przechowywania niestandardowego typu. Nie tworzysz zmiennych do przechowywania określonego typu. W ogóle nie „tworzysz” zmiennych. Nadajesz nazwy przedmiotom.
Druga uwaga: Python kieruje się bardzo prostą zasadą, jeśli chodzi o klasy, która jest w rzeczywistości znacznie bardziej spójna niż to, co robią języki takie jak Java, C ++ i C #: wszystko zadeklarowane wewnątrz class
bloku jest częścią klasy . Tak więc def
napisane tutaj funkcje ( ) są metodami, tj. Częścią obiektu klasy (nie są przechowywane dla poszczególnych instancji), podobnie jak w Javie, C ++ i C #; ale inne nazwiska tutaj również są częścią klasy. Nazwy są po prostu nazwami i nie mają powiązanych typów, a funkcje są również obiektami w Pythonie. A zatem:
class Example:
data = 42
def method(self): pass
Klasy też są obiektami w Pythonie.
Więc teraz stworzyliśmy obiekt o nazwie Example
, który reprezentuje klasę wszystkich rzeczy, które są Example
s. Ten obiekt ma dwa atrybuty dostarczane przez użytkownika (w C ++ „składowe”; w C # „pola, właściwości lub metody”; w języku Java „pola lub metody”). Jeden z nich ma nazwę data
i przechowuje wartość całkowitą 42
. Drugi nazywa się method
i przechowuje obiekt funkcji. (Istnieje kilka innych atrybutów, które Python dodaje automatycznie).
Jednak te atrybuty nadal nie są częścią obiektu. Zasadniczo obiekt jest tylko zbiorem większej liczby nazw (nazw atrybutów), dopóki nie przejdziesz do rzeczy, których nie można już podzielić. W ten sposób wartości mogą być współużytkowane między różnymi instancjami klasy, a nawet między obiektami różnych klas, jeśli celowo to ustawisz.
Utwórzmy instancję:
x = Example()
Teraz mamy oddzielny obiekt o nazwie x
, który jest instancją Example
. data
I method
faktycznie nie jest częścią obiektu, ale nadal możemy szukać ich poprzez x
powodu jakiejś magii, że Python robi za kulisami. W method
szczególności, gdy spojrzymy w górę , zamiast tego otrzymamy „metodę powiązaną” (kiedy ją wywołasz, x
zostanie przekazana automatycznie jako self
parametr, co nie może się zdarzyć, jeśli spojrzymy Example.method
bezpośrednio w górę ).
Co się dzieje, gdy próbujemy użyć x.data
?
Kiedy go badamy, najpierw znajduje się w obiekcie. Jeśli nie zostanie znaleziony w obiekcie, Python szuka w klasie.
Jednak kiedy przypiszemy do x.data
, Python utworzy atrybut na obiekcie. To będzie nie zastąpi atrybut class'.
To pozwala nam na inicjalizację obiektów . Python automatycznie wywoła __init__
metodę klasy w nowych instancjach, gdy zostaną utworzone, jeśli są obecne. W tej metodzie możemy po prostu przypisać do atrybutów, aby ustawić wartości początkowe dla tego atrybutu na każdym obiekcie:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
Teraz musimy określić, name
kiedy tworzymy Example
, a każda instancja ma swoją własną name
. Python zignoruje atrybut class Example.name
za każdym razem, gdy wyszukamy .name
instancję, ponieważ atrybut instancji zostanie znaleziony jako pierwszy.
Ostatnie zastrzeżenie: modyfikacja (mutacja) i przypisanie to dwie różne rzeczy!
W Pythonie ciągi znaków są niezmienne. Nie można ich modyfikować. Kiedy robisz:
a = 'hi '
b = a
a += 'mom'
Nie zmieniasz oryginalnego ciągu „cześć”. To niemożliwe w Pythonie. Zamiast tego tworzysz nowy łańcuch 'hi mom'
i powodujesz, a
że przestajesz być nazwą dla 'hi '
, a 'hi mom'
zamiast tego zaczynasz być nazwą . Stworzyliśmy b
również nazwę dla 'hi '
, a po ponownym zastosowaniu a
nazwy b
nadal jest nazwą dla 'hi '
, ponieważ 'hi '
nadal istnieje i nie została zmieniona.
Ale listy można zmienić:
a = [1, 2, 3]
b = a
a += [4]
Teraz b
jest również [1, 2, 3, 4], ponieważ nadaliśmy b
nazwę tej samej rzeczy, która została a
nazwana, a potem ją zmieniliśmy. Nie utworzyliśmy nowej listy dla a
do nazwania, ponieważ Python po prostu traktuje +=
listy inaczej.
Ma to znaczenie dla obiektów, ponieważ gdybyś miał listę jako atrybut klasy i używał instancji do modyfikowania listy, wtedy zmiana byłaby „widoczna” we wszystkich innych instancjach. Dzieje się tak, ponieważ (a) dane są w rzeczywistości częścią obiektu klasy, a nie obiektu instancji; (b) ponieważ modyfikowałeś listę i nie wykonywałeś prostego przypisania, nie utworzyłeś nowego atrybutu instancji, który ukrywałby atrybut klasy.