Nella pratica della programmazione è molto comune la tendenza ad usare variabili anche in espressioni dove si conosce a priori il loro valore: possiamo dire che i programmatori odiano i numeri e tendono a sostituirli sempre con variabili. In questo approfondimento vediamo il perchè esaminando qualche caso tipico.
Abbiamo visto nella Lezione 6 che l'oggetto Event, che rappresenta un evento di sistema passato
a pygame dal sistema operativo, ha un attributo type
che permette di identificarne il tipo. Che tipo di dato contiene
questo attributo? Proviamo ad aprire IDLE e digitiamo:
>>> import pygame >>> pygame.QUIT
256
>>> pygame.MOUSEBUTTONDOWN
1025
>>> pygame.KEYDOWN
768
Quindi i vari tipi di evento sono contraddistinti da un numero intero. Di solito questi numeri sono assegnati dal sistema operativo:
se ora volessimo verificare se un certo Event ev
è un clic del mouse dovremmo scrivere:
if ev.type == 1025:
E' facile capire che se il nostro programma dovesse monitorare parecchi tipi di evento diventerebbe difficile ricordare a quale numero
corrisponde ogni evento. Inoltre una persona che dovesse leggere il nostro programma farebbe parecchia fatica a capire il suo funzionamento.
Per questo gli sviluppatori di pygame hanno associato a ciascun tipo di evento una variabile diversa (QUIT
,
MOUSEBUTTONDOWN
, ...) con un nome facile da ricordare ed un valore corrispondente al numero dell'attributo type
.
In questo modo possiamo scrivere
if ev.type == MOUSEBUTTONDOWN:
al posto dell'istruzione precedente.
Sorge però un pronlema: cosa succederebbe se io usassi nel mio programma la variabile MOUSEBUTTONDOWN
, assegnandole un altro
valore? Evidentemente pygame non riconoscerebbe più i clic del mouse, e si provocherebbe una serie di errori strani e difficilmente
comprensibili.
Quello che abbiamo visto è un esempio dell'uso di costanti nei linguaggi di programmazione. Una costante è una
variabile di cui si conosce a priori il valore, e che non deve essere più modificata dopo la sua prima assegnazione. E' per questo che
alcuni linguaggi, come il C, hanno un'apposita parola chiave per rendere immodificabile una variabile: in C potremmo scrivere
const
davanti al nome della variabile quando le assegniamo un valore la prima volta e questo provocherebbe un errore se
tentassimo di modificarla.
In Python non c'è nulla del genere, tuttavia i programmatori hanno deciso di usare delle convenzioni, già derivate dall'uso nei linguaggi precedenti, che consentono di riconoscere facilmente le costanti e di evitare errori:
QUIT
, KEYUP
di pygame.Tipicamente le costanti in pygame si usano per le dimensioni di oggetti fissi, per i colori (al posto delle tuple RGB) o per i parametri di qualche funzione.
Quindi nella nostra pratica della programmazione dovremo ricordarci di non modificare mai una variabile scritta tutta in maiuscolo: come abbiamo detto Python permette di farlo, ma questa è una pratica pericolosissima perchè nelle intenzioni del programmatore quella variabile deve avere un valore fisso. Allo stesso modo, se nel nostro programma usiamo una costante, dovremo darle un nome scritto tutto in maiuscolo per far capire la situazione a chi legge il programma.
Rivediamo ora questo frammento, simile a quello che avevamo scritto nella Lezione 8 per far rimbalzare una Surface sui bordi della finestra:
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 600))
square = pygame.Surface((40, 40))
square.fill((240, 200, 20))
square_rect = square.get_rect()
square_vel = [1, 1]
. . .
if square_rect.left < 0 or square_rect.right > 800:
square_vel[0] *= -1
if square_rect.top < 0 or square_rect.bottom > 600:
square_vel[1] *= -1
. . .
Uno stile di programmazione simile, con tutti questi numeri, è considerato una bad practice dai programmatori: i numeri vengono chiamati ironicamente magic numbers e sono vivamente sconsigliati. Ricordiamo che nella realtà un programma complesso viene creato in genere da più persone che lavorano su file diversi, e che nel ciclo di vita di un programma capita di dover correggere errori, aggiungere funzionalità, rivedere il codice spesso scritto da un'altra persona, per cui è fondamentale che tale codice sia facilmente leggibile ed interpretabile. I problemi dei magic numbers sono i seguenti:
Anche qui possiamo risolvere questi problemi con l'uso delle costanti al posto dei numeri:
import pygame
from pygame.locals import *
SCREENSIZE = (800, 600)
SQUARESIZE = (40, 40)
SQUARECOLOR = (255, 240, 20)
pygame.init()
screen = pygame.display.set_mode(SCREENSIZE)
square = pygame.Surface(SQUARESIZE)
square.fill(SQUARECOLOR)
square_rect = square.get_rect()
square_vel = [1, 1]
. . .
if square_rect.left < 0 or square_rect.right > SCREENSIZE[0]:
square_vel[0] *= -1
if square_rect.top < 0 or square_rect.bottom > SCREENSIZE[1]:
square_vel[1] *= -1
. . .
Ho aggiunto le righe #4 #5 #6 nelle quali ho creato tre costanti. In questo modo ho ottenuto molteplci vantaggi:
SCREENSIZE[0]
e SCREENSIZE[1]
al posto di 800 e 600: ora se cambio il numero
800 nella #4 cambierà automaticamente anche il numero nella #17. Una regola da seguire
sempre è la seguente: se dobbiamo usare lo stesso numero più volte bisogna metterlo in una variabile:
eviteremo così situazioni come quella descritta precedentemente che provocano moltissimi errori ogni volta che si modifica il
codice.Potreste obiettare che ho dovuto aggiungere tre righe, allungando il programma: è vero che queste tecniche sono pensate soprattutto
per programmi molto grandi, nei quali spesso le costanti sono definite in un file apposito, che viene poi importato nel file principale.
E' proprio il caso di pygame, che definisce centinaia di costanti per gli eventi, i parametri delle funzioni, ecc.; l'istruzione
from pygame.locals import *
importa contemporaneamente tutti i loro nomi in modo da poterli usare senza anteporre il
nome del modulo.
Vi faccio notare che nel corso del tutorial non ho seguito questa strada per indicare le dimensioni dello schermo, preferendo
ottenerle ogni volta con i metodi .get_width()
e .get_height()
. Questo è preferibile perchè non presume
che le dimensioni della finestra rimangano immutate per tutto il programma (potreste avere ad esempio una finestra ridimensionabile).