Aplicació: Rellotge despertador

Més rellotges! Aquesta lliçó mostra com utilitzar una estructura per reunir en una sola variable els tres components hora, minuts i segons de l’hora del dia d’un rellotge.

Definició del tipus

Considereu un rellotge digital, que marca les hores, minuts i segons de l’hora del dia. Tal vegada voldríem desar aquesta informació en un sol lloc i, fer-ho en una estructura com la següent seria una idea ben adient:

@dataclass
class Hora:
    h: int = 0      # hora  (0..23)
    m: int = 0      # minut (0..59)
    s: int = 0      # segon (0..59)

És a dir, l’hora del dia té tres camps: h, m i s que representen, respectivament, les seves hores, minuts i segons. Cada camps és un enter, i als comentaris hem emfatitzat els valors legals que poden tenir cadascun d’ells. A més, hem donat un valor per defecte a cadascun dels camps.

Fixeu-vos que també haguéssim pogut definir una Hora com una llista amb tres enters, o amb una tupla de tres camps, però haver-ho fet amb una estructura ho fa molt més segur i explícit, ja que no hi haurà mai errors amb les posicions ni dubtes sobre quin valor hi ha a cada posició.

Construcció de valors

Un cop definit el tipus Hora, ja es pot construir-ne valors:

migdia = Hora(12, 0, 0)
mitjanit = Hora(0, 0, 0)

Sovint, els valors de classes s’anomenen instàncies. En aquest cas, migdia és una instància de Hora.

Al constructor, es poden passar els paràmetres reals especificant a quin paràmetre formal es refereixen. En aquest cas, també se’ls pot canviar l’ordre:

alarma = Hora(s=0, m=30, h=7)  # 07:30:00

A més, no cal donar els valors pels camps que ja els tenen per defecte:

alarma = Hora(m=30, h=7)    # 07:30:00
alarma = Hora(7, 30)        # 07:30:00

Operacions

Una primera operació que ens podria ser útil és la d’escriure una hora donada. Per això, escriurem una acció que, donada una Hora, l’escriu en format estàndard, amb dos dígits per camp utilitzant un text amb format:

def escriure_hora(hora: Hora) -> None:
    print(f'{hora.h:02d}:{hora.m:02d}:{hora.s:02d}')

A continuació, podríem tenir una operació que, donada un hora, li afegís un segon. Hi ha dues maneres de fer-ho: amb una acció que té un paràmetre que és modificat o amb una funció que, donada una hora, en retorna una de nova. Totes dues són opcions vàlides i la preferència de l’una sobre l’altra depèn del context.

Comencem amb l’acció que modifica el paràmetre:

def incrementar_un_segon(hora: Hora) -> None:
    hora.s += 1
    if hora.s == 60:
        hora.s = 0
        hora.m += 1
        if hora.m == 60:
            hora.m = 0
            hora.h += 1
            if hora.h == 24:
                hora.h = 0

I ara, fem-ho amb una funció:

def un_segon_mes_tard(hora: Hora) -> Hora:
    despres = dataclasses.replace(hora)
    despres.s += 1
    if despres.s == 60:
        despres.s = 0
        despres.m += 1
        if despres.m == 60:
            despres.m = 0
            despres.h += 1
            if despres.h == 24:
                despres.h = 0
    return despres

Aquesta vegada, cal copiar primer l’hora en una nova variable. La manera de fer-ho és amb dataclasses.replace. Ara que ja disposem d’una còpia del paràmetre real que podem modificar lliurament sense modificar l’original i treballem amb ella i, al final, la retornem.

Programa principal

Per acabar, fem un programa complet que simuli un rellotge despertador, escrivint l’hora corresponent cada segon i avisant a l’hora de l’alarma. Per a fer-ho, utilitzarem els subprogrames anteriors i la funció sleep() disponible al mòdul estàndard time que suspèn l’execució del programa durant tants segons com se li passi per paràmetre.

import time
import dataclasses

@dataclasses.dataclass
class Hora:
    h: int = 0       # hora  (0..23)
    m: int = 0       # minut (0..59)
    s: int = 0       # segon (0..59)

def escriure_hora(hora: Hora) -> None:
    print(f'{hora.h:02d}:{hora.m:02d}:{hora.s:02d}')

def incrementar_un_segon(hora: Hora) -> None:
    hora.s += 1
    if hora.s == 60:
        hora.s = 0
        hora.m += 1
        if hora.m == 60:
            hora.m = 0
            hora.h += 1
            if hora.h == 24:
                hora.h = 0

def main() -> None:
    hora = Hora(23, 59, 55)
    alarma = Hora(7, 30)
    while True:
        escriure_hora(hora)
        if hora == alarma:
            print('ring ring!')
        sleep(1)                       # esperar un segon
        incrementar_un_segon(hora)

if __name__ == '__main__':
    main()

L’ordre de les funcions no és rellevant en Python, però si que cal declarar abans el tipus Hora que les funcions que l’utilitzen.

El bucle while True és un bucle infinit: el programa no acabarà mai. Normalment no volem programes que mai acabin, però, en aquest cas… jo no voldria que el meu rellotge acabés! Piqueu control+c per aturar la seva execució.

La comparació de dues instàncies amb == consisteix en mirar si tots els seus camps són iguals. També es pot usar !=, però <, >, >= i <= (per defecte) no funcionen.




Lliçons.jutge.org
Jordi Petit
Universitat Politècnica de Catalunya, 2023

Prohibit copiar. Tots els drets reservats.
No copy allowed. All rights reserved.