Le Classes combinano dati e comportamento.
Le classi si usano per creare copie, o instances, di dati e comportamento raggruppati insieme, comunemente noti come objects.
Gli oggetti possono rappresentare entità del mondo reale (come automobili o gatti), oppure concetti più astratti (come numeri interi, veicoli o mammiferi).
Le classi sono parte integrante di un approccio di programmazione orientata agli oggetti (OOP), che chiede a chi programma di pensare a modellare un problema come uno o più oggetti che interagiscono tra loro, mantengono uno stato e agiscono sui dati.
Le classi sono le definizioni di nuovi tipi di oggetto, a partire dai quali si creano nuove instances di oggetti.
Spesso raggruppano dati (noti come fields, attributes, properties, data members o variables) insieme a codice o funzioni (note come methods) che operano su quei dati.
In questo senso, una classe è un progetto o un insieme di istruzioni da cui si possono costruire e usare molti oggetti di tipo simile.
Un programma complesso può avere molte classi, ognuna delle quali costruisce molti tipi diversi di oggetti.
Il processo di costruzione e personalizzazione di un oggetto a partire da una classe è noto come instantiation (o creazione di un'istanza della classe).
La definizione di una classe in Python è semplice:
class MyClass:
# Class body goes here
I campi di classe (altrimenti noti come proprietà, attributi, data member o variabili) possono essere aggiunti al corpo della classe.
class MyClass:
number = 5
string = "Hello!"
Si può creare un'istanza (oggetto) di MyClass e associarla a un nome:
>>> new_object = MyClass()
# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>
Gli Class attributes sono condivisi da tutti gli oggetti (o istanze) creati da una classe e sono accessibili tramite la dot notation, cioè un . posto dopo il nome dell'oggetto e prima del nome dell'attributo:
>>> new_object = MyClass()
# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5
# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'
# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()
>>> second_new_object
<__main__.MyClass at 0x15ad99970>
# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True
Gli attributi di classe si definiscono nel corpo della classe stessa, prima di qualsiasi altro metodo. Appartengono alla classe, il che permette di condividerli tra le istanze. Dato che questi attributi sono condivisi da tutte le istanze della classe, il loro valore può essere letto e modificato direttamente dalla classe. Modificare il valore degli attributi di classe cambia il valore per tutti gli oggetti istanziati dalla classe:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
# Accessing a class attribute from an object.
>>> obj_two.number
5
# Accessing the class attribute from the class itself.
>>> MyClass.number
5
# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27
# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27
>>> obj_two.number
27
Avere un mucchio di oggetti con dati sincronizzati in ogni momento non è particolarmente utile.
Per fortuna, gli oggetti creati da una classe possono essere personalizzati con i propri instance attributes (o proprietà, variabili o campi di istanza).
Come suggerisce il nome, gli attributi di istanza sono unici per ogni oggetto e possono essere modificati in modo indipendente.
__init__()
Lo speciale «dunder method» (abbreviazione di «double underscore method») __init__() serve a personalizzare le istanze di una classe e può essere usato per inizializzare attributi o proprietà di istanza degli oggetti.
Per il suo ruolo nell'inizializzazione degli attributi di istanza, __init__() è anche chiamato class constructor o initializer.
__init__() accetta un parametro obbligatorio chiamato self, che si riferisce all'oggetto appena creato.
I dati per gli attributi o le proprietà di istanza possono poi essere passati come argomenti di __init__(), dopo il parametro self.
Qui sotto, MyClass ha ora attributi di istanza chiamati location_x e location_y.
Come puoi vedere, i due attributi sono stati assegnati al primo e al secondo indice dell'argomento location (una tupla) passato a __init__().
Gli attributi location_x e location_y di un'istanza della classe verranno ora inizializzati quando istanzi la classe e viene creato un oggetto:
class MyClass:
# These are class attributes, variables, or fields.
number = 5
string = "Hello!"
# This is the class "constructor", with a "location" parameter that is a tuple.
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
# Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))
# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))
# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1
>>> new_object_two.location_x
-8
Tieni presente che devi passare un solo argomento quando inizializzi MyClass qui sopra: Python si occupa di passare self quando la classe viene chiamata.
Un altro modo per creare una variabile di istanza è accedere semplicemente a una variabile di classe tramite un oggetto e cambiarla in qualcos'altro:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'
>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'
>>> obj_one.string
'Hello!'
Ora guarda cosa succede quando la variabile di classe cambia:
>>> MyClass.string = "World!"
>>> obj_two.string
# obj_two.string has not changed
'Hi!'
>>> obj_one.string
# obj_one.string changed
'World!'
L'attributo string ora è una variabile di istanza nel caso di obj_two, ma resta una variabile di classe in obj_one.
Lo stesso si può fare anche dall'interno della classe:
class Demo:
new_var = 3
def add_two(self):
self.new_var += 2
Nel momento in cui viene chiamato <object>.add_two() e viene letto self.new_var += 2, new_var passa da variabile di classe a variabile di istanza con lo stesso nome.
Può essere utile durante l'inizializzazione, quando tutte le istanze di una classe hanno bisogno che uno o più attributi partano con lo stesso valore.
Tuttavia, la variabile di istanza poi oscura la variabile di classe, rendendo quest'ultima inaccessibile dall'istanza in cui è oscurata.
Vista la situazione, può essere più sicuro e più chiaro impostare gli attributi di istanza dal metodo __init__() come self.<attribute>.
Un method è una function associata alla classe stessa (noto come class method, di cui parleremo in dettaglio in un esercizio successivo) oppure a un'instance della classe (oggetto).
I metodi che operano su un oggetto o un'istanza vanno definiti con self come primo parametro.
Puoi poi definire il resto dei parametri come faresti per una funzione «normale» o non associata.
I metodi che operano su una classe vanno definiti con il decoratore @classmethod e (per convenzione) con cls come primo parametro.
I metodi di classe si chiamano direttamente sulla classe, senza creare prima un oggetto dalla classe.
class MyClass:
number = 5
string = "Hello!"
# Class constructor.
def __init__(self, location):
# Instance properties
self.location_x = location[0]
self.location_y = location[1]
# Class method. Uses the @classmethod decorator, and cls as the first parameter.
# Can be called without first making an instance of the class.
@classmethod
def change_string(cls, new_string):
#Class attributes are referred to with cls.
cls.string = new_string
return cls.string
# Instance method. Note "self" as first parameter.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
Come per l'accesso agli attributi, chiamare un metodo è semplice come mettere un . dopo il nome dell'oggetto e prima del nome del metodo.
Il metodo chiamato non ha bisogno di una copia dell'oggetto come primo parametro: Python inserisce self automaticamente:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)
# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)
Si può accedere agli attributi di classe dall'interno dei metodi di istanza allo stesso modo in cui vi si accede dall'esterno della classe:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5
>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6
pass
Negli esercizi sui concetti precedenti e negli stub degli esercizi di pratica, avrai visto la parola chiave pass usata dentro il corpo delle funzioni al posto del codice vero.
La parola chiave pass è un segnaposto sintatticamente valido: impedisce a Python di generare un errore di sintassi per una funzione o una definizione di classe vuota.
In sostanza, è un modo per dire all'interprete Python: «Non preoccuparti! Prima o poi metterò qui del codice, semplicemente non l'ho ancora fatto».
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
# Stubbing or place-holding the body of this method.
pass