Per què classes?
Al capítol d'estructures vas veure un dict fent de "struct": agrupant dades (nom, edat) sota un sol nom. Però les dades soles no basten — recorda aniversari(p), una funció a part que calia cridar passant-li el diccionari.
Una classe permet unir la dada i el comportament que hi actua: en lloc de aniversari(jordi), escriuràs jordi.aniversari() — l'objecte i les seves accions, junts.
Definir una classe
Exemple
class Persona:
def __init__(self, nom, edat):
self.nom = nom
self.edat = edat
p1 = Persona("Jordi", 16)
print(p1.nom, p1.edat)
__init__ és el constructor: s'executa automàticament en crear un objecte nou (Persona("Jordi", 16)), i és on li assignes els atributs inicials. self és l'objecte mateix — sempre el primer paràmetre de qualsevol mètode, encara que mai l'escriguis en cridar-lo.
p1 és una instància de la classe Persona: un objecte concret, amb els seus propis valors. Pots crear-ne tants com vulguis (p2 = Persona("Anna", 20)), cadascun independent.
Mètodes
Un mètode és una funció definida dins la classe, que actua sobre self.
class Persona:
def __init__(self, nom, edat):
self.nom = nom
self.edat = edat
def aniversari(self):
self.edat = self.edat + 1
p1 = Persona("Jordi", 16)
p1.aniversari()
print(p1.edat) # 17
Compara p1.aniversari() amb l'aniversari(jordi) del capítol d'estructures: el mateix efecte (incrementar l'edat), però ara la funció viu dins de la pròpia classe, i la crides sobre l'objecte directament.
__str__ com es mostra un objecte
Per defecte, print(p1) mostraria alguna cosa críptica com <__main__.Persona object at 0x...>. El mètode especial __str__ et deixa triar què es mostra.
class Persona:
def __init__(self, nom, edat):
self.nom = nom
self.edat = edat
def __str__(self):
return f"{self.nom} ({self.edat} anys)"
p1 = Persona("Jordi", 16)
print(p1) # Jordi (16 anys)
print() crida __str__ tot sol quan li passes un objecte: mai l'escrius tu mateix com p1.__str__().
bonus C++ també en té
Tot aquest material estava basat en C/C++, i C++ (a diferència de C) sí que té classes — amb una sintaxi semblant: class Persona { public: string nom; int edat; void aniversari() { edat++; } };. Els conceptes (constructor, mètodes, objectes) són els mateixos; només canvia la sintaxi concreta.
Mira els objectes crear-se i canviar pas a pas
Fixa't en com self sempre fa referència a l'objecte sobre el qual s'ha cridat el mètode.
Classes i objectes
Codi
Variables
| Nom | Valor |
|---|
Terminal
Problemes per resoldre
Sense solució ni comprovació automàtica: escriu el programa, executa'l (python3 programa.py) i prova'l amb els exemples. Les pistes són plegades perquè les obris només si les necessites.
1. Classe Rectangle
Defineix una classe Rectangle amb amplada i alcada, i dos mètodes: area() i perimetre().
Àrea: 20 Perímetre: 18
Pistes
def area(self): return self.amplada * self.alcada — un mètode no necessita cap paràmetre més enllà de self si tot el que li cal ja és als seus propis atributs.
2. Comptador de clics
Defineix una classe ContadorClics amb un comptador intern que comença a 0, un mètode clic() que l'incrementa, i un mètode valor() que el retorna.
0 1 2
Pistes
__init__(self) no necessita cap paràmetre més: simplement fa self.comptador = 0.
Crea un objecte, imprimeix c.valor(), fes c.clic() un parell de cops, imprimint c.valor() cada vegada.
3. Persona amb __str__
Defineix una classe Persona amb nom i edat, un mètode es_major_edat() que retorni True/False, i un __str__ que la mostri de forma llegible.
Jordi (16 anys) És major d'edat? False
Pistes
def es_major_edat(self): return self.edat >= 18.
Exercicis d'autoavaluació
Aquests es generen amb valors nous a l'atzar i tenen solució comprovable.
Quina serà la sortida per terminal d'aquest programa?