Va bene, per prima cosa.
Non esistono cose come "dichiarazione di variabili" o "inizializzazione di variabili" in Python.
Esiste semplicemente ciò che chiamiamo "assegnazione", ma probabilmente dovremmo chiamare semplicemente "denominazione".
Assegnazione significa "questo nome sul lato sinistro ora si riferisce al risultato della valutazione del lato destro, indipendentemente da ciò a cui si riferiva prima (semmai)".
foo = 'bar'
foo = 2 * 3
In quanto tali, i nomi di Python (un termine migliore di "variabili", probabilmente) non hanno tipi associati; i valori fanno. Puoi riapplicare lo stesso nome a qualsiasi cosa indipendentemente dal suo tipo, ma la cosa ha ancora un comportamento che dipende dal suo tipo. Il nome è semplicemente un modo per fare riferimento al valore (oggetto). Questo risponde alla tua seconda domanda: non crei variabili per contenere un tipo personalizzato. Non crei variabili per contenere un tipo particolare. Non "crei" affatto variabili. Dai nomi agli oggetti.
Secondo punto: Python segue una regola molto semplice quando si tratta di classi, che in realtà è molto più coerente di ciò che fanno linguaggi come Java, C ++ e C #: tutto ciò che viene dichiarato all'interno del class
blocco fa parte della classe . Quindi, le functions ( def
) scritte qui sono metodi, cioè parte dell'oggetto classe (non memorizzato su una base per istanza), proprio come in Java, C ++ e C #; ma altri nomi qui sono anche parte della classe. Di nuovo, i nomi sono solo nomi e non hanno tipi associati e anche le funzioni sono oggetti in Python. Quindi:
class Example:
data = 42
def method(self): pass
Anche le classi sono oggetti , in Python.
Quindi ora abbiamo creato un oggetto denominato Example
, che rappresenta la classe di tutte le cose che sono Example
s. Questo oggetto ha due attributi forniti dall'utente (in C ++, "membri"; in C #, "campi o proprietà o metodi"; in Java, "campi o metodi"). Uno di questi è denominato data
e memorizza il valore intero 42
. L'altro è denominato method
e memorizza un oggetto funzione. (Ci sono molti altri attributi che Python aggiunge automaticamente.)
Tuttavia, questi attributi non fanno ancora parte dell'oggetto. Fondamentalmente, un oggetto è solo un insieme di più nomi (i nomi degli attributi), finché non si arriva a cose che non possono più essere suddivise. Pertanto, i valori possono essere condivisi tra diverse istanze di una classe o anche tra oggetti di classi diverse, se lo si imposta deliberatamente.
Creiamo un'istanza:
x = Example()
Ora abbiamo un oggetto separato denominato x
, che è un'istanza di Example
. Le data
e method
non fanno effettivamente parte dell'oggetto, ma possiamo comunque cercarle tramite a x
causa di qualche magia che Python fa dietro le quinte. Quando guardiamo in alto method
, in particolare, otterremo invece un "metodo vincolato" (quando lo chiamiamo, x
viene passato automaticamente come self
parametro, cosa che non può accadere se cerchiamo Example.method
direttamente).
Cosa succede quando proviamo a utilizzare x.data
?
Quando lo esaminiamo, viene prima cercato nell'oggetto. Se non viene trovato nell'oggetto, Python cerca nella classe.
Tuttavia, quando assegniamo a x.data
, Python creerà un attributo sull'oggetto. Essa non sostituirà l'attributo di classe.
Questo ci consente di eseguire l' inizializzazione degli oggetti . Python chiamerà automaticamente il __init__
metodo della classe sulle nuove istanze quando vengono create, se presenti. In questo metodo, possiamo semplicemente assegnare agli attributi per impostare i valori iniziali per quell'attributo su ogni oggetto:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
Ora dobbiamo specificare a name
quando creiamo un Example
, e ogni istanza ha la sua name
. Python ignorerà l'attributo class Example.name
ogni volta che cerchiamo l' .name
istanza di un'istanza, perché l'attributo dell'istanza verrà trovato per primo.
Un ultimo avvertimento: la modifica (mutazione) e l'assegnazione sono cose diverse!
In Python, le stringhe sono immutabili. Non possono essere modificati. Quando lo fai:
a = 'hi '
b = a
a += 'mom'
Non si modifica la stringa "hi" originale. Questo è impossibile in Python. Invece, crei una nuova stringa 'hi mom'
e fai a
smettere di essere un nome per 'hi '
e inizi a essere un nome per 'hi mom'
invece. Abbiamo fatto b
un nome anche per 'hi '
, e dopo aver applicato nuovamente il a
nome, b
è ancora un nome per 'hi '
, perché 'hi '
esiste ancora e non è stato cambiato.
Ma gli elenchi possono essere modificati:
a = [1, 2, 3]
b = a
a += [4]
Ora b
è anche [1, 2, 3, 4], perché abbiamo creato b
un nome per la stessa cosa che ha a
chiamato, e poi abbiamo cambiato quella cosa. Non abbiamo creato un nuovo elenco per a
nominare, perché Python tratta semplicemente in modo +=
diverso per gli elenchi.
Questo è importante per gli oggetti perché se avessi un elenco come attributo di classe e utilizzassi un'istanza per modificare l'elenco, la modifica sarebbe "visibile" in tutte le altre istanze. Questo perché (a) i dati fanno effettivamente parte dell'oggetto classe e non di un oggetto istanza; (b) poiché stavi modificando l'elenco e non eseguendo un semplice assegnamento, non hai creato un nuovo attributo di istanza che nasconde l'attributo di classe.