Introducció als mòduls de funcions

En el primer objecte d’aprenentatge d’aquesta lliçó s’introduirà el concepte de mòdul de funcions amb la construcció de mòdul de funcions parachute. En el segon, es parlarà de la guia d’estil per fer els programes seguint les recomanacions oficials per al Python. Ara que els vostres programes ja incorporaran diversos fitxers, important mòduls, definint funcions, i efectuant crides a codi definit en altres fitxers, és el moment de començar a ser més rigorosos pel que fa a la nomenclatura i documentació de mòduls, funcions, constants i qualsevol altre aspecte d’un programa de Python.

Mòdul de funcions

Ja coneixeu la importància de descompondre problemes complexos en tasques més petites i modulars mitjançant l’ús de funcions. Això no obstant, a mesura que el vostre codi creix, és probable que us enfronteu a un conjunt de funcions interrelacionades que formen part d’un mateix concepte o funcionalitat. Aquestes funcions, fins ara, poden estar disperses en diferents fitxers, el que pot fer complicat gestionar el vostre projecte a mesura que creix.

Per facilitar la gestió del codi i millorar la reutilització s’utilitzen els mòduls de funcions.

Què és un mòdul de funcions

Un mòdul de funcions en Python és senzillament un fitxer amb extensió .py que conté un conjunt de definicions, com ara funcions, constants i classes. Aquestes definicions poden ser importades i utilitzades en altres programes o mòduls.

En altres paraules, un mòdul és una manera d’organitzar el codi en fragments lògics i reutilitzables. Per a què, i quan fer servir mòduls de funcions? Imagineu que esteu treballant en un projecte de programari i teniu un conjunt de funcions relacionades que formen part d’una mateixa tasca o funcionalitat. Si totes aquestes funcions estan en un sol fitxer, esdevé fàcil localitzar-les i entendre la seva interconnexió. També és més senzill fer proves i mantenir-les.

No obstant això, a mesura que el vostre projecte creix, mantenir totes les funcions en un únic fitxer pot arribar a ser desorganitzat i poc pràctic. Aquí és on entren en joc els mòduls de funcions:

  • Organització: agrupant funcions relacionades en un mateix mòdul, manteniu el vostre codi organitzat i fàcil de gestionar.
  • Reutilització: podeu importar aquest mòdul a altres parts del vostre projecte o fins i tot a altres projectes, permetent la reutilització de codi sense haver de reescriure les mateixes funcions una vegada i una altra.

Per a facilitar la feina posareu totes les funcions relacionades amb algun tema en un mateix fitxer i creareu un mòdul de funcions. D’aquesta manera només caldrà importar el mòdul (igual que importeu el mòdul math o qualsevol altre ja definit en el sistema) per poder utilitzar totes les seves funcions.

Exemple de mòdul de funcions predefinit en Python

Python ja ve amb una gran quantitat de mòduls estàndard que contenen funcions útils per a una àmplia varietat de tasques. En aquest apartat es farà una breu exploració del mòdul math de Python.

El mòdul de funcions math

El mòdul math de Python és una eina potent per a realitzar tasques matemàtiques. Proporciona una àmplia gamma de funcions per fer operacions matemàtiques avançades. Ja l’heu utilitzat, i només se’n farà un breu recordatori.

Entreu a l’intèrpret interactiu i utilitzeu algunes de les funcions de math, com les següents:

>>> import math
>>> math.sqrt(4)
2.0
>>> math.pi
3.141592653589793
>>> 
Com podeu observar, els mòduls de Python poden tenir funcions i altres coses. En aquest cas, el mòdul math conté un atribut que s’anomena pi (fa el paper d’una constant, tot i que no ho acaba de ser, i per això no està definida en majúscules).

Si necessiteu informació o documentació sobre el mòdul i les seves funcions, sabeu que podeu obtenir-la mitjançant l’ordre help(). Si dins de l’intèrpret interactiu teclegeu l’ordre help() (després d’haver importat el mòdul), obtindreu una pàgina com la següent:

Help on built-in module math:

NAME
    math

DESCRIPTION
    This module provides access to the mathematical functions
    defined by the C standard.

FUNCTIONS
    acos(x, /)
        Return the arc cosine (measured in radians) of x.
        
        The result is between 0 and pi.

On hi ha el nom i una breu descripció del mòdul i a continuació el docstring de totes les funcions definides.

Si us desplaceu fins al final de la pàgina observareu la descripció següent:

La decisió d’utilitzar minúscules per a les constants com e o pi en el mòdul math de Python és simplement una convenció de nomenclatura seguida per mantenir la coherència amb altres parts del llenguatge Python i per a la claredat del codi. Per convenció, i com que el processador de Python no distingeix entre constants i variables, es va decidir que les del mòdul math fossin en minúscules.

    ulp(x, /)
        Return the value of the least significant bit of the float x.

DATA
    e = 2.718281828459045
    inf = inf
    nan = nan
    pi = 3.141592653589793
    tau = 6.283185307179586

FILE
    (built-in)

Podeu observar que aquí és on es troba definida la constant pi.

Creació del primer mòdul de funcions

L’objectiu d’aquest apartat serà crear el primer mòdul de funcions. S’utilitzarà com a exemple el programa traductor del codi parachute realitzat en la lliçó anterior.

Programa original (amb les funcions en el mateix fitxer)

L’enunciat és el següent: escriure un programa que serveixi per descodificar missatges escrits en un paracaigudes que compleixen les següents condicions:

  • Unes parts del paracaigudes són de color taronja, i altres són de color blanc.
  • Cada bocí de color taronja, descrit amb la lletra ‘V’, s’interpretarà com al bit 1, i cada bocí del color blanc, descrit amb la lletra ‘B’ com al bit 0. S’interpretarà el codi en grups 7 bocins.
  • Davant de cada grup s’hi ha afegit un separador. Un separador són tres bocins de color blanc.
  • Cada línia de missatge representa una paraula.
  • El codi per a desxifrar el grup i convertir-lo a caràcter és el següent: el grup que, en base 2, representa el nombre decimal 1 correspon a la lletra ‘A’, el que representa el 2 correspon a la ‘B’, etc…

El codi del programa que vau elaborar és el següent (el fitxer s’anomenarà parachute.py):

def baseb_to_deci(num_b, b):
	'''
	Funció que passa un nombre escrit en una base b a decimal
	Entrada: str, int(num_b: cadena en base b correcta. Lletres minúscules,
	base major o igual a 2
	Sortida: int
	'''
	
	total = 0
	exponent = len(num_b) - 1
	for car in num_b:
		
		if car >= '0' and car <= '9':
			total = total + int(car) * b ** exponent		
		else:
			#trampeta
			total = total + (ord(car) - ord('a') + 10) * b ** exponent
	
		exponent = exponent - 1
	return total
	
def convertir_a_binari(para):
	'''
	Funció que passa un nombre escrit en parachute binari
	Entrada: str (Codi B/V)
	Sortida: str (codi binari)
	'''
	ZERO = 'B'
	UN = 'V'
	
	binari = ''
	for c in para:
		if c == ZERO:
			carac = '0'
		else:
			carac = '1'
		binari = binari + carac
	return binari
	
def traductor(missatge_x):
	'''
	Funció que tradueix un missatge xifrat en codi "parachute" a text.
	El missatge té separadors entre lletres de longitud 3 i s'escriu emprant
	per a cada caràcter una cadena de longitud 7.
	Entrada: str (missatge xifrat segons el codi (correcte))
	Sortida: str (Missatge desxifrat)
	'''
	LEN_PACK = 7
	LEN_SEP = 3	
	
	missatge_d = ''

	llargada = LEN_SEP + LEN_PACK
	# retallo segons els caràcters
	for i in range(0, len(missatge_x), llargada):
		pack = missatge_x[i + LEN_SEP: i + llargada]
		# passo a binari un set
		bina = convertir_a_binari(pack)
		# passo de binari a decimal
		deci = baseb_to_deci(bina, 2)
		# desxifro el caràcter
		car = desxifrar_car(deci)
		missatge_d = missatge_d + car
		
	return missatge_d
	
def desxifrar_car(n):
	'''
	Retorna el caràcter corresponent al codi numèric segons el nostre xifratge
	En aquest cas, la posició de la lletra majúscula
	Entrada: int entre 1 i 26
	Sortida: str (lletra majúscula corresponent)
	'''
	LLETRES = '0ABCDEFGHIJKLMNOPQRSTUVWXYZ'  #LA A val 1
	return LLETRES[n]
	
# Programa principal
print(traductor(input()))
print(traductor(input()))
print(traductor(input()))

Aquest és el codi del programa principal, però recordeu que per arribar fins aquí vau haver de definir cadascuna de les funcions en un fitxer diferent per codificar-les i testar-les amb el seu test-driver.

Imagineu que hi ha una nova missió al Pol Nord, i la NASA decideix utilitzar un nou paracaigudes amb un nou missatge codificat. Però en aquesta ocasió, el paracaigudes portarà un nombre escrit que indicarà la quantitat de paraules que s’han de descodificar. És evident que necessitareu moltes funcions que ja teniu definides (en aquest cas totes), per a escriure un nou programa principal que serà lleugerament diferent.

El pas que fareu ara és simular que torneu a codificar el programa, però creant progressivament el mòdul de funcions. Finalment, podreu comprovar que utilitzant mòduls de funcions és molt senzill reutilitzar el codi, i sereu capaços de desxifrar tots els missatges dels paracaigudes de la NASA.

Documentació

Per començar, seguiu les instruccions següents:

  1. Esborreu el programa principal del fitxer parachute.py.
  2. Situeu el directori actiu allà on tingueu el fitxer desat, i entreu a l’intèrpret interactiu.
  3. Importeu el mòdul parachute.
  4. Utilitzeu alguna de les funcions del mòdul, per comprovar que s’hi pot accedir.
  5. Escriviu la comanda help(paracute)
>>> import parachute
>>> parachute.desxifrar_car(3)
'C'
>>> help(parachute)

Si us ha funcionat el codi anterior, haureu obtingut una pàgina semblant a la del mòdul math, amb els docstrings de les funcions del mòdul parachute.

Help on module parachute:

NAME
    parachute

FUNCTIONS
    baseb_to_deci(num_b, b)
        Funció que passa un nombre escrit en una base b a decimal
        Entrada: str, int(num_b: cadena en base b correcta. Lletres minúscules,
        base major o igual a 2
        Sortida: int
    
    convertir_a_binari(para)
        Funció que passa un nombre escrit en parachute binari
        Entrada: str (Codi B/V)
        Sortida: str (codi binari)
    
    desxifrar_car(n)
        Retorna el caràcter corresponent al codi numèric segons el nostre xifratge
        En aquest cas, la posició de la lletra majúscula
        Entrada: int entre 1 i 26
        Sortida: str (lletra majúscula corresponent)
    
    traductor(missatge_x)
        Funció que tradueix un missatge xifrat en codi ‘parachute’ a text.
        El missatge té separadors entre lletres de longitud 3 i s'escriu emprant
        per a cada caràcter una cadena de longitud 7.
        Entrada: str (missatge xifrat segons el codi (correcte))
        Sortida: str (Missatge desxifrat)

FILE
    /../treball/parachute.py

Com s’ha dit al principi, un mòdul de funcions, en Python no és més que un fitxer qualsevol de Python (amb l’extensió .py) en el que hi ha d’haver alguna funció definida. Una vegada important el mòdul, es té accés a les funcions definides del vostre mòdul exactament igual que es fa amb les dels mòduls predefinits.

Comentaris i docstrings

El primer que s’ha de tenir en compte és la documentació. Com heu comprovat, l’ordre help proporciona una descripció per cadascuna de les funcions definides. Però no és màgica, l’explicació que ofereix és la que heu proporcionat al docstring. O més ben dit, ofereix la documentació que el programador ha escrit entre tres cometes simples (o dobles).

Descripció del mòdul

La primera millora que fareu en el mòdul parachute serà escriure una descripció indicant per a què serveix i per a què s’utilitza. Cal afegir tot el que considereu que pugui ser d’interès per a qui hagi d’utilitzar aquest mòdul de funcions. Aquest comentari s’afegeix a l’inici, i és el que l’ordre help() mostrarà com a descripció.

En aquest exemple, es podria afegir el següent comentari descriptiu, a l’inici del fitxer, sense indentar.

Si feu un canvi en un mòdul, no n’hi ha prou amb importar-lo de nou per obtenir la nova versió. És necessari sortir i tornar a entrar a l’intèrpret interactiu.

'''
Aquest mòdul conté funcions per treballar amb 'codi parachute'.

El codi parachute consisteix en grups de 10 caràcters escollits entre B i V. 
Cada caràcter comença per un separador de 'BBB' i consta de 7 lletres.
Per traduir el grup s'ha de passar a codi binari, calcular el valor decimal i
buscar la lletra corresponent de l'abecedari
'''

Si afegiu el comentari, sortiu de l’intèrpret interactiu, torneu a importar el mòdul i cridar a l’ajuda, veureu la descripció al principi de la pàgina.

Escollir en quin mòdul incorporar una nova funció

Els mòduls serveixen per endreçar i organitzar les funcions relacionades, de manera que sigui fàcil per al programador localitzar la funció quan la necessiti. És important definir cada funció on toca establint criteris lògics.

En el mòdul parachute hi ha d’haver les funcions que treballen amb el codi parachute. Però no és el lloc adequat per desar la funció baseb_to_deci(). És una funció que ja heu utilitzat més vegades en àmbits molt més generals que per a desxifrar missatges de paracaigudes, i quan necessiteu reutilitzar-la, serà complicat que la trobeu si la deseu en aquest mòdul.

En començar a resoldre un programa, i una vegada s’han escrit els docstrings de les funcions a definir, és el moment per decidir en quin mòdul cal codificar cada nova funció.

En l’exemple que s’està dissenyant, la decisió correcta serà incloure les funcions convertir_a_binari(), desxifrar_car() i traductor() en un mòdul creat per al programa, el mòdul parachute. Però la funció baseb_to_deci() no hauria d’estar en aquest fitxer. Caldrà afegir-la en un mòdul propi on hi hagi funcions de matemàtiques o semblants, i importar el mòdul matemàtiques des d’on es necessiti. Fixeu-vos en l’exemple següent:

Mòdul de funcions que importa un altre mòdul

Creeu un nou mòdul de funcions en un nou fitxer, i l’anomeneu matematiques.py. Per invocar aquest mòdul des del fitxer parachute.py, només cal importar-lo a l’inici del fitxer amb la instrucció import. A partir d’aquest moment, per utilitzar la funció caldrà escriure el nom del mòdul precedint al de la funció. El codi del mòdul parachute seria:

import matematiques

def traductor(missatge_x):
	'''
	Funció que tradueix un missatge xifrat en codi "parachute" a text.
	El missatge té separadors entre lletres de longitud 3 i s'escriu emprant
	per a cada caràcter una cadena de longitud 7.
	Entrada: str (missatge xifrat segons el codi (correcte))
	Sortida: str (Missatge desxifrat)
	'''
	LEN_PACK = 7
	LEN_SEP = 3	
	
	missatge_d = ''

	llargada = LEN_SEP + LEN_PACK
	# retallo segons els caràcters
	for i in range(0, len(missatge_x), llargada):
		pack = missatge_x[i + LEN_SEP: i + llargada]
		# passo a binari un set
		bina = convertir_a_binari(pack)
		# passo de binari a decimal
		deci = matematiques.baseb_to_deci(bina, 2) #crida a al modul matematiques
		# desxifro el caràcter
		car = desxifrar_car(deci)
		missatge_d = missatge_d + car
		
	return missatge_d

A continuació hi anirien les altres funcions del mòdul.

Observeu la instrucció:

deci = matematiques.baseb_to_deci(bina, 2) #crida a al modul matematiques
On es fa la crida a la funció baseb_to_deci() del mòdul matematiques.

Test-drivers en un mòdul de funcions

Abans de definir cada funció, cal haver fet tant el seu docstring com el test-driver. Aquest últim també es considera documentació important, de manera que és interessant mantenir-lo per poder revisar-lo quan es consideri oportú. El lloc on ha d’estar tota aquesta informació és el mòdul de funcions.

La primera idea per a elaborar el mòdul de funcions, mantenint un joc de proves per a cadascuna d’elles, és escriure el test-driver sota la definició de cada funció, com es veu en l’exemple següent:

Mòdul mal estructurat

def desxifrar_car(n):
	'''
	Retorna el caràcter corresponent al codi numèric segons el nostre xifratge
	En aquest cas, la posició de la lletra majúscula
	Entrada: int entre 1 i 26
	Sortida: str (lletra majúscula corresponent)
	'''
	LLETRES = '0ABCDEFGHIJKLMNOPQRSTUVWXYZ'  #LA A val 1
	return LLETRES[n]
# test-driver desxifrar_caracter
print(desxifrar_car(1))
print(desxifrar_car(4))

def convertir_a_binari(para):
	'''
	Funció que passa un nombre escrit en parachute binari
	Entrada: str (Codi B/V)
	Sortida: str (codi binari)
	'''
	# codi
# test-driver convertir_a_binari

El motiu principal pel qual no es pot estructurar el mòdul com en l’exemple, és perquè cada test-driver és un programa principal, i si feu això dividiu el programa principal en diversos bocins, un a sota de cada funció. No es pot fer per convenció, tot i que per al processador de Python no representa cap problema.

El segon motiu és que, com veureu a continuació, quan s’importa un mòdul s’executa el programa principal, llevat que ordeneu no fer-ho. I per evitar que s’executi, cal tenir tot el programa principal definit en un sol bloc.

Separar el programa principal

Per seguir amb l’exemple i simplificar-lo, en comptes d’utilitzar la funció baseb_to_deci() i evitar importar el mòdul matemàtiques, s’utilitzarà la funció del sistema int(), que permet, també, el pas d’un str que signifiqui un nombre de qualsevol base a decimal.

Amb les instruccions que s’han esmentat fins ara, el mòdul parachute és el següent:

'''
Aquest mòdul conté funcions per treballar amb "codi parachute".
El codi parachute consisteix en grups de 10 caràcters escollits entre B i V. Cada caràcter comença per un separador de 'BBB' i consta de 7 lletres.
Per traduir el grup s'ha de passar a codi binari, calcular el valor decimal i buscar la lletra corresponent de l'abecedari
'''
	
def convertir_a_binari(para):
	'''
	Funció que passa un nombre escrit en parachute binari
	Entrada: str (Codi B/V)
	Sortida: str (codi binari)
	'''
	ZERO = 'B'
	UN = 'V'
	
	binari = ''
	for c in para:
		if c == ZERO:
			carac = '0'
		else:
			carac = '1'
		binari = binari + carac
	return binari
		
def desxifrar_car(n):
	'''
	Retorna el caràcter corresponent al codi numèric segons el nostre xifratge
	En aquest cas, la posició de la lletra majúscula
	Entrada: int entre 1 i 26
	Sortida: str (lletra majúscula corresponent)
	'''
	LLETRES = '0ABCDEFGHIJKLMNOPQRSTUVWXYZ'  #LA A val 1
	return LLETRES[n]
	
def traductor(missatge_x):
	'''
	Funció que tradueix un missatge xifrat en codi ‘parachute’ a text.
	El missatge té separadors entre lletres de longitud 3 i s'escriu emprant
	per a cada caràcter una cadena de longitud 7.
	Entrada: str (missatge xifrat segons el codi (correcte))
	Sortida: str (Missatge desxifrat)
	'''
	LEN_PACK = 7
	LEN_SEP = 3	
	
	missatge_d = ''

	llargada = LEN_SEP + LEN_PACK
	# retallo segons els caràcters
	for i in range(0, len(missatge_x), llargada):
		pack = missatge_x[i + LEN_SEP: i + llargada]
		# passo a binari un set
		bina = convertir_a_binari(pack)
		# passo de binari a decimal
		deci = int(bina, 2) #funció del sistema
		# desxifro el caràcter
		car = desxifrar_car(deci)
		missatge_d = missatge_d + car
		
	return missatge_d
	
# test-drivers	
# test-driver convertir_a_binari
print(convertir_a_binari('BBVV'))
print(convertir_a_binari('BBVVBVBV'))
print()
# test-driver desxifrar_caracter
print(desxifrar_car(1))
print(desxifrar_car(4))
print()
# test-driver traductor
print(traductor('BBBBBBBVBBBBBBBBBBBVBBBBBVBBVBBBBBBBBVBV'))
print(traductor('BBBBBBBBBVBBBBBBBBVB'))

Observeu què passa quan s’importa des de l’intèrpret interactiu:

>>> import parachute
0011
00110101

A
D

DARE
AB

En importar el mòdul s’executa el programa principal. I això també succeirà quan es faci la importació per a executar un programa de la manera habitual.

Per evitar que el programa principal d’un fitxer s’executi a l’importar-lo, cal utilitzar la instrucció.

if __name__ == "__main__":

Aquesta instrucció s’utilitza per condicionar l’execució del codi principal del mòdul només quan aquest és executat com a script principal i no quan és importat com a mòdul per un altre programa.

Significat de la instrucció if __name__ == "__main__"

__name__ és una variable especial en Python que conté el nom del mòdul. Quan el mòdul s’està executant com a script principal, el valor de __name__ és “__main__”. Quan el mòdul s’importa en un altre programa, el valor de __name__ serà el nom real del mòdul.

Per tant, la instrucció if __name__ == “__main__” : comprova si el mòdul s’està executant com a script principal. Si és així, el bloc de codi dins d’aquesta condició s’executarà. Si el mòdul s’està utilitzant com a mòdul importat, el bloc de codi dins d’aquesta condició no s’executarà.

Tant per convertir un programa qualsevol en mòdul de funcions, com per crear un mòdul amb els test-drivers de les funcions, és necessari l’ús d’aquesta instrucció. Tenint en compte totes aquestes consideracions, el mòdul parachute, fins al moment, presentarà el següent aspecte:

'''
Aquest mòdul conté funcions per treballar amb "codi parachute".
El codi parachute consisteix en grups de 10 caràcters escollits entre B i V. Cada caràcter comença per un separador de 'BBB' i consta de 7 lletres.
Per traduir el grup s'ha de passar a codi binari, calcular el valor decimal i buscar la lletra corresponent de l'abecedari
'''
	
def convertir_a_binari(para):
	'''
	Funció que passa un nombre escrit en parachute binari
	Entrada: str (Codi B/V)
	Sortida: str (codi binari)
	'''
	ZERO = 'B'
	UN = 'V'
	
	binari = ''
	for c in para:
		if c == ZERO:
			carac = '0'
		else:
			carac = '1'
		binari = binari + carac
	return binari
		
def desxifrar_car(n):
	'''
	Retorna el caràcter corresponent al codi numèric segons el nostre xifratge
	En aquest cas, la posició de la lletra majúscula
	Entrada: int entre 1 i 26
	Sortida: str (lletra majúscula corresponent)
	'''
	LLETRES = '0ABCDEFGHIJKLMNOPQRSTUVWXYZ'  #LA A val 1
	return LLETRES[n]
	
def traductor(missatge_x):
	'''
	Funció que tradueix un missatge xifrat en codi ‘parachute’ a text.
	El missatge té separadors entre lletres de longitud 3 i s'escriu emprant
	per a cada caràcter una cadena de longitud 7.
	Entrada: str (missatge xifrat segons el codi (correcte))
	Sortida: str (Missatge desxifrat)
	'''
	LEN_PACK = 7
	LEN_SEP = 3	
	
	missatge_d = ''

	llargada = LEN_SEP + LEN_PACK
	# retallo segons els caràcters
	for i in range(0, len(missatge_x), llargada):
		pack = missatge_x[i + LEN_SEP: i + llargada]
		# passo a binari un set
		bina = convertir_a_binari(pack)
		# passo de binari a decimal
		deci = int(bina, 2) #funció del sistema
		# desxifro el caràcter
		car = desxifrar_car(deci)
		missatge_d = missatge_d + car
		
	return missatge_d
	
if __name__ == "__main__":
	# test-drivers	
	# test-driver convertir_a_binari
	print(convertir_a_binari('BBVV'))
	print(convertir_a_binari('BBVVBVBV'))
	print()
	# test-driver desxifrar_caracter
	print(desxifrar_car(1))
	print(desxifrar_car(4))
	print()
	# test-driver traductor
	print(traductor('BBBBBBBVBBBBBBBBBBBVBBBBBVBBVBBBBBBBBVBV'))
	print(traductor('BBBBBBBBBVBBBBBBBBVB'))

Fixeu-vos en els dos exemples d’ús del fitxer parachute.py i les diferències entre ambdós.

Execució de parachute.py com a programa principal

Si executeu el fitxer com a un programa, el resultat mostra els test-drivers de les funcions definides.

$ python3 parachute.py 
0011
00110101

A
D

DARE
AB

Ús del mòdul parachute

Si importeu el mòdul parachute, podeu fer ús de les seves funcions sense que s’executi el programa principal del fitxer parachute.py

>>> import parachute
>>> parachute.convertir_a_binari('BBV')
'001'

Endreçar els test-driver

Finalment, s’aconsella, en el moment de crear un mòdul i a mesura que es van incorporant noves funcions, mantenir un ordre. Si en el moment d’incorporar una nova funció, executeu el mòdul com a programa principal se us mostren tots els test-drivers de les funcions anteriors, com a humans us resultarà difícil destriar les proves que us interessen.

Per evitar el “soroll” dels tes-drivers de les funcions anteriors s’utilitzarà una instrucció if:

if True:
  #test-driver de la nova funció

Tot i que sembli una instrucció absurda, us ajudarà quan definiu la vint-i-setena funció d’un mòdul i us molesti veure els vint-i-sis test-drivers anteriors. En aquest cas, només caldrà modificar el booleà dels anteriors test-drivers a False i no es mostraran.

El que és absolutament imprescindible és desar el test-driver en el mòdul per si en algun moment necessiteu repassar el joc de proves d’alguna de les funcions definides.

Test-driver del mòdul parachute

Suposeu que heu anat creant el mòdul parachute pas a pas, començant per les funcions menors. Heu codificat per últim la funció traductor(), i us molesten els resultats dels jocs de proves de les funcions anteriors.

Només cal que modifiqueu el booleà dels test-drivers anteriors a False, i en executar el mòdul només obtindreu els resultats del joc de proves de la funció desitjada. El codi del programa principal del mòdul parachute, en aquest cas, serà el següent:

if __name__ == "__main__":
	# test-drivers	
	if False:
		# test-driver convertir_a_binari
		print(convertir_a_binari('BBVV'))
		print(convertir_a_binari('BBVVBVBV'))
		print()
		
	if False:	
		# test-driver desxifrar_caracter
		print(desxifrar_car(1))
		print(desxifrar_car(4))
		print()
		
	if True:
		# test-driver traductor
		print(traductor('BBBBBBBVBBBBBBBBBBBVBBBBBVBBVBBBBBBBBVBV'))
		print(traductor('BBBBBBBBBVBBBBBBBBVB'))

I aquest el resultat de l’execució:

$ python3 parachute.py 
DARE
AB
Com podeu observar, només es mostren les proves de l’última funció.

Ús del mòdul per a diversos programes

Ja teniu el mòdul de funcions. Ara veurem com un mòdul facilita l’elaboració de programes amb les seves funcions. Quan s’utilitzen les funcions d’un mòdul, si una funció en crida d’altres, aquestes estaran incloses en el mòdul. Com que en programar-les, a mesura que s’afegeixen les funcions al mòdul, es van testant amb el test-driver, ja podem estar segurs que hi seran.

Descodificació del paracaigudes del Perseverance

En el programa d’aquest exemple, cal descodificar el codi del fitxer següent (corresponent a les tres circumferències concèntriques internes del paracaigudes):

BBBBBBBVBBBBBBBBBBBVBBBBBVBBVBBBBBBBBVBV
BBBBBBVVBVBBBBBBVBBVBBBBBBBVVVBBBBBBVBBBBBBBBVBVBBBBBBBVVBBV
BBBBBVBVBBBBBBBBVBBBBBBBBBVBBVBBBBBBVVVBBBBBBBBVVVBBBBBVBBVV
El codi del programa descodificador perseverance.pyserà:
import parachute

print(parachute.traductor(input()))
print(parachute.traductor(input()))
print(parachute.traductor(input()))

I el resultat de l’execució:

videoioc@debian-xtec:~/Documents/programacio/treball$ python3 perseverance.py < missatge_paracaigudes.txt 
DARE
MIGHTY
THINGS

Descodificador del paracaigudes de la missió al Pol Nord

En una nova missió al Pol Nord, la NASA decideix utilitzar un nou paracaigudes amb un nou missatge codificat. Aquest paracaigudes utilitza el mateix codi que el del paracaigudes del Perseverance. Però en aquesta ocasió, el paracaigudes portarà un nombre escrit que indicarà la quantitat de paraules que s’han de descodificar. I la intenció serà llançar diversos paracaigudes, cadascun amb el seu propi missatge.

El primer paracaigudes utilitzat en aquesta nova missió porta el nombre 1, i la successió cromàtica mostrada en el fitxer (després del nombre 1):

1
BBBBBVBVBBBBBBBBVBBBBBBBBBVBBVBBBBBBVVVBBBBBBBBVVVBBBBBVBBVV

Aquest és el programa us permetrà desxifrar el codi dels paracaigudes del Pol Nord.

import parachute

quantitat = int(input())
for i in range(0, quantitat):
	print(parachute.traductor(input()))
El missatge amagat en aquesta ocasió és molt críptic. Proveu-lo i el trobareu.

La guia d'estil PEP 8

El vostre codi ja comença a ocupar un volum considerable, i un programa ja consta de més d’un fitxer, amb importacions, definicions de funcions i altres elements que afegeixen complexitat. Cal definir unes recomanacions, una guia d’estil per facilitar a altres programadors (i a vosaltres mateixos en altres etapes de la vida) la comprensió del codi que creeu.

Python ofereix poques restriccions amb relació a l’ordre en què es poden escriure diferents fragments de codi en un programa. La majoria de llenguatges són determinats per blocs i no permeten, per exemple, definir una funció en mig del programa principal.

Una de les idees principals de Guido van Rossum, el creador de Python, és que el codi es llegeix molt més sovint del que s’escriu. Per tant, és essencial escriure codi que sigui llegible i consistent. Les directrius proporcionades en la Guia d’estil de Python, coneguda com a PEP 8, tenen com a objectiu millorar la llegibilitat del codi i mantenir la seva consistència en tot el codi Python.

A continuació es recullen les recomanacions més adequades al vostre nivell de programació de la guia PEP 8:

Longitud de la línia

  • La longitud màxima de la línia ha de ser de màxim 80 caràcters.

Parèntesis

  • Es recomana utilitzar els parèntesis només quan són necessaris en les operacions, excepte en l’ús dels operadors booleans (and, or i not), on es poden utilitzar per facilitar la llegibilitat.
  • Està bé, tot i que no és necessari, utilitzar parèntesis al voltant de tuples.
  • No utilitzar-los en opcions de retorn o condicionals (excepte per indicar una tupla).

Indentació

  • Indentar els blocs de codi amb 4 espais.
  • Mai una barreja de tabulacions i espais.

Línies en blanc

  • Deixar una línia en blanc entre definicions de funcions.
  • No hi ha d’haver cap línia en blanc després d’un def.

Espais en blanc

  • No hi ha d’haver espais en blanc abans d’una coma, i sí que n’hi ha d’haver després (com en les regles tipogràfiques estàndard).
  • No hi ha d’haver espais en blanc entre parèntesis o claudàtors.
  • Posar sempre espais a banda i banda entre aquests operadors binaris: assignació ( = ), increment (+=, -= etc.), comparacions (==, <, >, !=, <>, ⇐, >= ) ni booleans (and, or, not).
  • No posar parèntesis en les crides a mètodes.

Comentaris i docstrings

  • Utilitzar el caràcter # per a comentaris del refinament o per proporcionar un aclariment puntual.
  • Utilitzar el comentari de tres caràcters de cometes simples o dobles només en els docstrings i comentaris en els mòduls.

Cadenes

  • És indiferent utilitzar cometes simples o dobles en strings. Però si es comença amb un tipus, s’ha de continuar amb el tipus escollit.
  • Utilitzar una f-string per donar format a les cadenes.

imports

  • Es recomanen els imports absoluts. Són més llegibles i proporcionen missatges d’error millors si el sistema no està ben configurat.
  • Cada importació s’ha de fer en una línia separada.
  • S’importaran primer els mòduls del sistema i després els propis.
  • S’han d’escriure sempre al principi del fitxer, just després dels comentaris i la documentació.

Un import absolut és la instrucció:

import modul

No es recomana importar funcions concretes, com en el cas següent:

from modul import funcio

Constants

  • Les constants es defineixen després dels imports i abans de definir les funcions.

Noms

  • Els noms de les funcions, els noms de les variables i els noms de fitxer han de ser descriptius.

Programa principal

  • El programa principal s’escriu en un sol bloc després de les definicions de les funcions.
  • Si un fitxer està destinat a ser utilitzat com a mòdul de funcions, el codi sempre ha de comprovar
    if __name__ == ‘__main__’

    de manera que no s’executi quan s’importa el mòdul.

Anar a la pàgina anterior:
Referències