Da quello che hai scritto, ti manca una parte fondamentale della comprensione: la differenza tra una classe e un oggetto. __init__
non inizializza una classe, inizializza un'istanza di una classe o di un oggetto. Ogni cane ha un colore, ma i cani come classe no. Ogni cane ha quattro o meno piedi, ma la classe dei cani no. La classe è un concetto di un oggetto. Quando vedi Fido e Spot, riconosci la loro somiglianza, la loro dogma. Questa è la classe.
Quando dici
class Dog:
def __init__(self, legs, colour):
self.legs = legs
self.colour = colour
fido = Dog(4, "brown")
spot = Dog(3, "mostly yellow")
Stai dicendo, Fido è un cane marrone con 4 zampe mentre Spot è un po 'storpio ed è per lo più giallo. La __init__
funzione è chiamata costruttore, o inizializzatore, e viene chiamata automaticamente quando si crea una nuova istanza di una classe. All'interno di quella funzione, l'oggetto appena creato viene assegnato al parametro self
. La notazione self.legs
è un attributo chiamato legs
dell'oggetto nella variabile self
. Gli attributi sono un po 'come le variabili, ma descrivono lo stato di un oggetto o particolari azioni (funzioni) disponibili per l'oggetto.
Tuttavia, nota che non sei pronto colour
per la dogità stessa: è un concetto astratto. Ci sono attributi che hanno senso nelle classi. Ad esempio, population_size
è uno di questi - non ha senso contare il Fido perché Fido è sempre uno. Ha senso contare i cani. Diciamo che ci sono 200 milioni di cani nel mondo. È di proprietà della classe Dog. Fido non ha nulla a che fare con il numero 200 milioni, né Spot. Si chiama "attributo di classe", al contrario di "attributi di istanza" che sono colour
o legs
superiori.
Ora, a qualcosa di meno canino e più legato alla programmazione. Mentre scrivo di seguito, la classe per aggiungere cose non è sensata: di cosa è una classe? Le classi in Python sono costituite da raccolte di dati diversi, che si comportano in modo simile. La classe di cani è composta da Fido e Spot e 199999999998 altri animali simili a loro, tutti che fanno pipì sui lampioni. In cosa consiste la classe per aggiungere cose? In base a quali dati inerenti a loro differiscono? E quali azioni condividono?
Tuttavia, i numeri ... quelli sono argomenti più interessanti. Dì, numeri interi. Ce ne sono molti, molto di più dei cani. So che Python ha già degli interi, ma facciamo il finto tonto e "implementiamoli" di nuovo (barando e usando gli interi di Python).
Quindi, gli interi sono una classe. Hanno alcuni dati (valore) e alcuni comportamenti ("aggiungimi a questo altro numero"). Mostriamo questo:
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
Questo è un po 'fragile (supponiamo other
che sarà un MyInteger), ma ora lo ignoreremo. Nel codice reale, non lo faremmo; lo testeremmo per esserne sicuri, e magari lo forzeremmo ("non sei un intero? perbacco, hai 10 nanosecondi per diventarlo! 9 ... 8 ....")
Potremmo anche definire frazioni. Anche le frazioni sanno come sommarsi.
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)
Ci sono anche più frazioni che numeri interi (non proprio, ma i computer non lo sanno). Facciamo due:
half = MyFraction(1, 2)
third = MyFraction(1, 3)
five_sixths = half.add(third)
print five_sixths.numerator
# ==> 5
print five_sixths.denominator
# ==> 6
In realtà non stai dichiarando nulla qui. Gli attributi sono come un nuovo tipo di variabile. Le variabili normali hanno solo un valore. Diciamo che scrivi colour = "grey"
. Non puoi avere un'altra variabile denominata colour
che sia "fuchsia"
- non nella stessa posizione nel codice.
Gli array lo risolvono fino a un certo punto. Se dici colour = ["grey", "fuchsia"]
di aver impilato due colori nella variabile, ma li distingui in base alla loro posizione (0 o 1, in questo caso).
Gli attributi sono variabili legate a un oggetto. Come con gli array, possiamo avere molte colour
variabili, su cani diversi . Quindi, fido.colour
è una variabile, ma spot.colour
è un'altra. Il primo è legato all'oggetto all'interno della variabile fido
; il secondo spot
,. Ora, quando chiami Dog(4, "brown")
, o three.add(five)
, ci sarà sempre un parametro invisibile, che verrà assegnato a quello extra penzolante all'inizio dell'elenco dei parametri. Viene chiamato convenzionalmente self
e otterrà il valore dell'oggetto davanti al punto. Quindi, all'interno del Dog's __init__
(costruttore), self
ci sarà qualunque cosa risulterà essere il nuovo Dog; all'interno di MyInteger
's add
, self
sarà associato all'oggetto nella variabile three
. Così,three.value
sarà la stessa variabile all'esterno di add
, come self.value
all'interno di add
.
Se lo dico the_mangy_one = fido
, inizierò a fare riferimento all'oggetto noto come fido
con un altro nome. D'ora in poi, fido.colour
è esattamente la stessa variabile di the_mangy_one.colour
.
Quindi, le cose all'interno del file __init__
. Puoi pensare a loro come a annotazioni nel certificato di nascita del cane. colour
di per sé è una variabile casuale, potrebbe contenere qualsiasi cosa. fido.colour
o self.colour
è come un campo modulo sul foglio di identità del cane; ed __init__
è l'impiegato che lo compila per la prima volta.
Qualcosa di più chiaro?
EDIT : Espandendo il commento qui sotto:
Intendi una lista di oggetti , vero?
Prima di tutto, in fido
realtà non è un oggetto. È una variabile, che attualmente contiene un oggetto, proprio come quando dici x = 5
, x
è una variabile che attualmente contiene il numero cinque. Se in seguito cambi idea, puoi farlo fido = Cat(4, "pleasing")
(a patto di aver creato una classe Cat
) e fido
da quel momento in poi "conterrai" un oggetto gatto. Se lo fai fido = x
, conterrà il numero cinque e non sarà affatto un oggetto animale.
Una classe di per sé non conosce le sue istanze a meno che tu non scriva specificamente codice per tenerne traccia. Per esempio:
class Cat:
census = [] #define census array
def __init__(self, legs, colour):
self.colour = colour
self.legs = legs
Cat.census.append(self)
Ecco census
un attributo di Cat
classe a livello di classe.
fluffy = Cat(4, "white")
spark = Cat(4, "fiery")
Cat.census
# ==> [<__main__.Cat instance at 0x108982cb0>, <__main__.Cat instance at 0x108982e18>]
# or something like that
Nota che non otterrai [fluffy, sparky]
. Quelli sono solo nomi di variabili. Se vuoi che i gatti stessi abbiano un nome, devi creare un attributo separato per il nome e quindi sovrascrivere il __str__
metodo per restituire questo nome. Lo scopo di questo metodo (cioè la funzione associata alla classe, proprio come add
o __init__
) è descrivere come convertire l'oggetto in una stringa, come quando lo stampi.