W tym, co napisałeś, brakuje ci krytycznego zrozumienia: różnicy między klasą a przedmiotem. __init__
nie inicjalizuje klasy, inicjuje wystąpienie klasy lub obiektu. Każdy pies ma kolor, ale psy jako klasa nie. Każdy pies ma cztery lub mniej łap, ale klasa psów nie. Klasa to pojęcie obiektu. Kiedy widzisz Fido i Spot, rozpoznajesz ich podobieństwo, ich psią budę. To jest klasa.
Kiedy powiesz
class Dog:
def __init__(self, legs, colour):
self.legs = legs
self.colour = colour
fido = Dog(4, "brown")
spot = Dog(3, "mostly yellow")
Mówisz, że Fido to brązowy pies z 4 nogami, podczas gdy Spot jest trochę kaleką i jest przeważnie żółty. __init__
Wywoływana jest funkcja konstruktora lub inicjator i jest automatycznie wywoływana podczas tworzenia nowej instancji klasy. W ramach tej funkcji nowo utworzony obiekt jest przypisywany do parametru self
. Notacja self.legs
to wywoływany atrybut legs
obiektu w zmiennej self
. Atrybuty są czymś w rodzaju zmiennych, ale opisują stan obiektu lub określone akcje (funkcje) dostępne dla obiektu.
Zauważ jednak, że nie jesteś nastawiony colour
na samą psiarnię - to abstrakcyjna koncepcja. Istnieją atrybuty, które mają sens w klasach. Na przykład population_size
jest jednym z nich - nie ma sensu liczyć Fido, ponieważ Fido zawsze jest jednym. Liczenie psów ma sens. Powiedzmy, że na świecie jest 200 milionów psów. To własność klasy Dog. Fido nie ma nic wspólnego z liczbą 200 milionów, podobnie jak Spot. Nazywa się to „atrybutem klasy”, w przeciwieństwie do „atrybutów instancji”, które są równe colour
lub legs
wyższe.
A teraz coś mniej psiego, a bardziej związanego z programowaniem. Jak piszę poniżej, klasa dodawania rzeczy nie ma sensu - co to za klasa? Klasy w Pythonie składają się ze zbiorów różnych danych, które zachowują się podobnie. Klasa psów składa się z Fido i Spot oraz 199999999998 innych podobnych do nich zwierząt, wszystkie sikają na latarniach. Z czego składa się klasa dodawania rzeczy? Jakimi danymi się różnią? A jakie akcje mają wspólne?
Jednak liczby… to ciekawsze tematy. Powiedz, liczby całkowite. Jest ich dużo, dużo więcej niż psy. Wiem, że Python ma już liczby całkowite, ale zagrajmy w głupków i „zaimplementujmy” je ponownie (oszukując i używając liczb całkowitych Pythona).
Tak więc liczby całkowite są klasą. Mają pewne dane (wartość) i pewne zachowania („dodaj mnie do tego innego numeru”). Pokażmy to:
class MyInteger:
def __init__(self, newvalue)
# imagine self as an index card.
# under the heading of "value", we will write
# the contents of the variable newvalue.
self.value = newvalue
def add(self, other):
# when an integer wants to add itself to another integer,
# we'll take their values and add them together,
# then make a new integer with the result value.
return MyInteger(self.value + other.value)
three = MyInteger(3)
# three now contains an object of class MyInteger
# three.value is now 3
five = MyInteger(5)
# five now contains an object of class MyInteger
# five.value is now 5
eight = three.add(five)
# here, we invoked the three's behaviour of adding another integer
# now, eight.value is three.value + five.value = 3 + 5 = 8
print eight.value
# ==> 8
Jest to trochę kruche (zakładamy, że other
będzie to MyInteger), ale teraz zignorujemy. W prawdziwym kodzie nie; przetestowalibyśmy to, aby się upewnić, a może nawet wymusilibyśmy to („nie jesteś liczbą całkowitą? przez golly, masz 10 nanosekund, aby stać się jednością! 9 ... 8 ....”)
Moglibyśmy nawet zdefiniować ułamki. Frakcje również wiedzą, jak się dodawać.
class MyFraction:
def __init__(self, newnumerator, newdenominator)
self.numerator = newnumerator
self.denominator = newdenominator
# because every fraction is described by these two things
def add(self, other):
newdenominator = self.denominator * other.denominator
newnumerator = self.numerator * other.denominator + self.denominator * other.numerator
return MyFraction(newnumerator, newdenominator)
Jest jeszcze więcej ułamków niż liczb całkowitych (niezupełnie, ale komputery o tym nie wiedzą). Zróbmy dwa:
half = MyFraction(1, 2)
third = MyFraction(1, 3)
five_sixths = half.add(third)
print five_sixths.numerator
# ==> 5
print five_sixths.denominator
# ==> 6
W rzeczywistości niczego tu nie deklarujesz. Atrybuty są jak nowy rodzaj zmiennych. Zmienne normalne mają tylko jedną wartość. Powiedzmy, że piszesz colour = "grey"
. Nie możesz mieć innej zmiennej o colour
takiej nazwie"fuchsia"
- nie w tym samym miejscu w kodzie.
Tablice do pewnego stopnia to rozwiązują. Jeśli powiesz colour = ["grey", "fuchsia"]
, że umieściłeś dwa kolory w zmiennej, ale rozróżniasz je na podstawie ich pozycji (w tym przypadku 0 lub 1).
Atrybuty to zmienne powiązane z obiektem. Podobnie jak w przypadku tablic, możemy mieć wiele colour
zmiennych dla różnych psów . Tak więc fido.colour
jest jedną zmienną, ale spot.colour
jest inną. Pierwsza jest związana z obiektem w zmiennej fido
; drugi, spot
. Teraz, gdy wywołasz Dog(4, "brown")
lub three.add(five)
zawsze będzie niewidoczny parametr, który zostanie przypisany do wiszącego dodatkowego parametru na początku listy parametrów. Jest konwencjonalnie nazywany self
i pobiera wartość obiektu przed kropką. Tak więc w Dog __init__
(konstruktor) self
będzie to, czym okaże się nowy Dog; wewnątrz MyInteger
's add
, self
zostanie powiązany z obiektem w zmiennej three
. A zatem,three.value
będzie ta sama zmienna poza add
, jak self.value
w add
.
Jeśli powiem the_mangy_one = fido
, zacznę odwoływać się do przedmiotu znanego pod fido
jeszcze inną nazwą. Od teraz fido.colour
jest dokładnie tą samą zmienną, co the_mangy_one.colour
.
Więc rzeczy wewnątrz __init__
. Możesz myśleć o nich jako o zapisywaniu rzeczy w akcie urodzenia Psa. colour
sama w sobie jest zmienną losową, może zawierać wszystko. fido.colour
lub self.colour
jest jak pole formularza na karcie identyfikacyjnej Psa; i __init__
czy urzędnik wypełnia go po raz pierwszy.
Jakieś jaśniejsze?
EDYCJA : Rozwinięcie komentarza poniżej:
Masz na myśli listę obiektów , prawda?
Przede wszystkim fido
właściwie nie jest przedmiotem. Jest to zmienna, która aktualnie zawiera obiekt, tak jak mówisz x = 5
, x
jest zmienną zawierającą obecnie liczbę pięć. Jeśli później zmienisz zdanie, możesz to zrobić fido = Cat(4, "pleasing")
(o ile utworzyłeś klasę Cat
) i fido
odtąd „zawiera” obiekt kota. Jeśli to zrobisz fido = x
, będzie zawierał liczbę pięć, a nie obiekt zwierzęcy.
Klasa sama w sobie nie zna swoich instancji, chyba że specjalnie napiszesz kod, aby je śledzić. Na przykład:
class Cat:
census = [] #define census array
def __init__(self, legs, colour):
self.colour = colour
self.legs = legs
Cat.census.append(self)
Tutaj census
jest atrybut Cat
klasy na poziomie klasy.
fluffy = Cat(4, "white")
spark = Cat(4, "fiery")
Cat.census
# ==> [<__main__.Cat instance at 0x108982cb0>, <__main__.Cat instance at 0x108982e18>]
# or something like that
Pamiętaj, że nie dostaniesz [fluffy, sparky]
. To tylko nazwy zmiennych. Jeśli chcesz, aby koty same miały imiona, musisz utworzyć osobny atrybut dla nazwy, a następnie zastąpić __str__
metodę zwracania tej nazwy. Celem tej metody (tj. Funkcji związanej z klasą, tak jak add
lub __init__
) jest opisanie, jak przekształcić obiekt w łańcuch, na przykład podczas drukowania.