Classi e Oggetti in Python
Scopri come funzionano classi e oggetti in Python: definisci classi, usa __init__, crea istanze, gestisci attributi e metodi e comprendi l'ereditarietà.
Python è un linguaggio di programmazione orientato agli oggetti (OOP), il che significa che organizza il codice attorno agli oggetti anziché alle sole funzioni. Una classe è il modello che descrive quali dati contiene un object e cosa può fare. Un object è un'istanza specifica creata da quel modello.
Questo capitolo tratta:
- Cosa sono le classi e gli oggetti e perché sono importanti
- Come definire una classe con
__init__, attributi di istanza e metodi - Attributi di classe versus attributi di istanza
- Come creare e utilizzare gli oggetti
- Come modificare ed eliminare attributi
- Come verificare i tipi di oggetti con
isinstance() - Una prima panoramica sull'ereditarietà
Per un approfondimento sull'ereditarietà, consulta Python Inheritance. Per pattern OOP più avanzati, consulta Python Abstract Base Classes.
Cos'è una Classe?
Pensa a una classe come a una formina per biscotti e a un object come al biscotto. Definisci la forma una volta sola (la classe), poi stampi quanti biscotti (oggetti) ti servono. Ogni object condivide la stessa struttura ma conserva i propri dati.
class Dog:
pass # an empty class — valid but not very useful yetLa parola chiave class seguita da un nome e dai due punti crea una nuova classe. Per convenzione, i nomi delle classi usano il formato CapWords (detto anche PascalCase): MyClass, BankAccount, HttpRequest.
Il Metodo __init__
Il metodo __init__ (abbreviazione di initialise) viene eseguito automaticamente ogni volta che crei un nuovo object. Imposta lo stato iniziale dell'object assegnando valori ai suoi attributi di istanza.
class Dog:
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attributeIl primo parametro è sempre self — fa riferimento all'object che viene creato. Python lo passa automaticamente; non devi mai fornirlo tu stesso.
Attributi di Istanza vs. Attributi di Classe
| Tipo | Definito dentro | Appartiene a | Condiviso? |
|---|---|---|---|
| Attributo di istanza | __init__ (tramite self) | Ogni object | No — ogni object ha la propria copia |
| Attributo di classe | Corpo della classe (fuori da qualsiasi metodo) | La classe stessa | Sì — tutti gli oggetti condividono un'unica copia |
class Dog:
species = "Canis familiaris" # class attribute — shared by all Dogs
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attribute
fido = Dog("Fido", 3)
bella = Dog("Bella", 5)
print(fido.species) # Canis familiaris
print(bella.species) # Canis familiaris (same class attribute)
print(fido.name) # Fido
print(bella.name) # Bella (different instance attributes)Usa un attributo di classe quando un valore è lo stesso per ogni object di quel tipo (ad es. la specie di tutti i cani). Usa gli attributi di istanza per i dati che variano per ogni object.
Definire i Metodi
Un metodo è una funzione definita all'interno di una classe. Come __init__, riceve sempre self come primo parametro in modo da poter accedere agli attributi dell'object.
class Dog:
species = "Canis familiaris"
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
return f"{self.name} says: Woof!"
def description(self):
return f"{self.name} is {self.age} years old."Un metodo si chiama su un object usando la notazione punto — Python passa automaticamente l'object come self:
fido = Dog("Fido", 3)
print(fido.bark()) # Fido says: Woof!
print(fido.description()) # Fido is 3 years old.Il Metodo __str__
Python chiama __str__ quando passi un object a print() o a str(). Senza di esso, ottieni un indirizzo poco utile come <__main__.Dog object at 0x...>. Definirlo rende il debug molto più semplice.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Dog(name={self.name!r}, age={self.age})"
fido = Dog("Fido", 3)
print(fido) # Dog(name='Fido', age=3)Creare e Utilizzare Oggetti
Creare un object si chiama istanziazione. Si chiama la classe come una funzione, passando gli argomenti che __init__ si aspetta (escluso self):
Puoi creare quanti oggetti vuoi dalla stessa classe — ognuno è indipendente:
class Rectangle:
def __init__(self, width, height=1):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
r1 = Rectangle(4, 3)
r2 = Rectangle(5) # height defaults to 1
print(r1.area()) # 12
print(r1.perimeter()) # 14
print(r2.area()) # 5Nota che height=1 fornisce un valore predefinito: se ometti il secondo argomento, Python usa automaticamente 1.
Modificare ed Eliminare Attributi
Puoi modificare o aggiungere attributi a un object dopo che è stato creato, ed eliminarli con del:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(p.age) # 30
p.age = 31 # modify an existing attribute
print(p.age) # 31
p.email = "[email protected]" # add a new attribute at runtime
print(p.email) # [email protected]
del p.email # delete the attribute
# print(p.email) # would raise AttributeErrorSebbene Python consenta di aggiungere attributi liberamente, è più pulito dichiarare tutti gli attributi all'interno di __init__ in modo che la struttura della classe sia evidente a colpo d'occhio.
Verificare i Tipi degli Oggetti
Usa isinstance() per verificare se un object è un'istanza di una particolare classe. Restituisce True per la classe stessa e per tutte le sue classi padre:
class Animal:
pass
class Dog(Animal):
pass
rex = Dog()
print(isinstance(rex, Dog)) # True
print(isinstance(rex, Animal)) # True — Dog is a subclass of Animal
print(type(rex) is Dog) # True
print(type(rex) is Animal) # False — type() does not climb the hierarchyPreferisci isinstance() rispetto a type() is nella maggior parte del codice, perché gestisce correttamente le sottoclassi.
Una Prima Panoramica sull'Ereditarietà
L'ereditarietà consente a una nuova classe di riutilizzare ed estendere il comportamento di una classe esistente. La nuova classe (figlia o sottoclasse) ottiene automaticamente tutti gli attributi e i metodi della classe padre:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} makes a sound."
class Dog(Animal):
def speak(self): # override the parent method
return f"{self.name} says: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow!"
dog = Dog("Rex")
cat = Cat("Whiskers")
print(dog.speak()) # Rex says: Woof!
print(cat.speak()) # Whiskers says: Meow!
print(isinstance(dog, Animal)) # TrueSia Dog che Cat ereditano __init__ da Animal, quindi non hanno bisogno di ridefinirlo. Sovrascrivono solo speak() per fornire un comportamento specifico alla razza.
Per un trattamento completo dell'ereditarietà — inclusi super(), l'ereditarietà multipla e l'ordine di risoluzione dei metodi — consulta Python Inheritance.
Quando Usare una Classe?
Le classi hanno più senso quando:
- Hai dati e comportamenti che stanno insieme (un conto bancario che sa come depositare e prelevare).
- Hai bisogno di più oggetti indipendenti dello stesso tipo (molti oggetti
Dog, ognuno con nome ed età diversi). - Vuoi modellare entità del mondo reale con un'identità chiara.
Le semplici funzioni di utilità che si limitano a trasformare l'input in output spesso funzionano meglio come funzioni semplici. Python non richiede che tutto sia in una classe.
Riepilogo
| Concetto | Cosa fa |
|---|---|
class | Definisce un nuovo tipo |
__init__ | Inizializza gli attributi di istanza quando viene creato un object |
self | Fa riferimento all'object corrente all'interno di un metodo |
| Attributo di istanza | Dato appartenente a un singolo object specifico |
| Attributo di classe | Dato condiviso tra tutti gli oggetti della classe |
| Metodo | Una funzione definita all'interno di una classe; riceve sempre self |
__str__ | Controlla come print() visualizza l'object |
isinstance() | Verifica se un object è un'istanza di una classe o delle sue sottoclassi |
| Ereditarietà | Consente a una classe figlia di riutilizzare ed estendere una classe padre |