Ús avançat de diccionaris
Els diccionaris són una estructura de dades fonamental en Python que permeten emmagatzemar informació en parelles de clau-valor, com si fos una base de dades en miniatura. En nivells inicials, els heu utilitzat per gestionar dades de forma senzilla, però, quan es treballa amb aplicacions reals, sovint es troben estructures de dades més complexes. És aquí on els diccionaris de diccionaris esdevenen especialment útils. En aquesta lliçó es va més enllà de l’ús bàsic dels diccionaris i se’n presenten aplicacions més sofisticades i eficients.
Tècniques d'ordenació de diccionaris
L’objectiu d’aquesta lliçó és oferir-vos eines per gestionar i manipular diccionaris amb dades complexes, com per exemple diccionaris que contenen altres diccionaris o llistes dins dels valors, així com tècniques d’ordenació i consulta segons diferents atributs dels elements.
Diccionari de diccionaris
En Python, un diccionari de diccionaris és un diccionari en què cada valor associat a una clau pot ser al seu torn un altre diccionari. Aquesta estructura permet organitzar i emmagatzemar dades de forma jeràrquica i facilita l’accés i manipulació d’informació complexa.
Un diccionari de diccionaris és especialment útil quan es gestionen col·leccions d’objectes amb múltiples atributs. Per exemple, podríeu voler guardar informació d’un grup d’estudiants en un únic diccionari, on cada estudiant té un identificador únic i dades com el nom, l’edat i les notes.
Per començar, s’estudiarà l’exemple següent:
alumnes = { '12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8, 10]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]} }
Cada alumne (representat en el diccionari principal amb la clau del DNI), conté un conjunt de dades que el representen mitjançant un diccionari secundari (un per alumne). La característica de tots aquests diccionaris secundaris és que tots tenen la mateixa mida i exactament les mateixes claus.
Es podria resumir amb aquest esquema:
alumnes = {dni: diccionari_alumne}
On el valor
diccionari_alumne = {'nom':nom, edat:edat, 'notes':notes}
Accés al diccionari secundari
A continuació es farà un repàs, amb exemples, de l’ús de les operacions bàsiques sobre diccionaris, en el cas concret en què el valor del diccionari sigui un altre diccionari. Utilitzeu l’intèrpret interactiu per verificar els exemples. Accedir al diccionari secundari d’un dels alumnes no ha de representar cap problema, ja que és el valor corresponent a la clau del DNI. Per exemple, per obtenir el diccionari secundari de l’alumne amb DNI 12345678:
>>> alumnes = {'12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]}} >>> alumnes['12345678A'] {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}
Accés a les dades d'un diccionari secundari
Per accedir a les dades, primer cal especificar la clau principal i després la clau interna dins del diccionari secundari.
Suposeu que voleu obtenir el nom i l’edat de l’estudiant amb DNI 12345678A.
>>>alumnes = { '12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8, 10]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]} } >>> alumnes = {'12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]}} >>> alumnes['12345678A']['nom'] 'Maria' >>> alumnes['12345678A']['edat'] 20
Afegir o modificar dades
Es poden afegir nous estudiants o modificar les dades dels existents utilitzant l’assignació per clau. Per exemple, en el codi següent s’afegeix un tercer estudiant i s’actualitza l’edat de la Maria.
>>> alumnes = {'12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]}} >>> alumnes['12345678A']['edat'] = 21 >>> alumnes['98765432C'] = {'nom': 'Joan', 'edat': 21, 'notes': [4, 5, 6]} >>> alumnes {'12345678A': {'nom': 'Maria', 'edat': 21, 'notes': [6, 7, 8]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]}, '98765432C': {'nom': 'Joan', 'edat': 21, 'notes': [4, 5, 6]}}
Ara alumnes conté tres estudiants, i l’edat de la Maria s’ha actualitzat a 21.
Accés a elements amb més complexitat
Si voleu accedir a un element d’una llista dins d’un diccionari de diccionaris, podeu fer-ho amb múltiples claus i índexs.
Per exemple, per obtenir la primera nota de l’estudiant 87654321B, s’ha d’accedir dins del diccionari alumnes, al diccionari secundari alumne. I dins d’aquest, a un element de la llista de notes.
>>> alumnes = {'12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]}} >>> alumnes['87654321B']['notes'][0] 5
Aquest tipus d’accés es pot utilitzar per consultar valors específics dins de diccionaris de diccionaris, independentment de la complexitat de les dades.
Recorregut d’un diccionari de diccionaris
Un dels avantatges d’aquesta estructura és que es poden fer servir bucles per recórrer les dades de forma estructurada i organitzada.
Iteració pels ítems d’un diccionari
>>> for dni, dades in alumnes.items(): ... f'{dni}, {dades["nom"]}' ... '12345678A, Maria' '87654321B, Pere'
En aquest exemple items() permet accedir a les parelles clau-valor (DNI i diccionari de dades) en el diccionari alumnes. En el diccionari secundari (representat amb la variable dades), només cal utilitzar la clau nom per accedir al nom de cada alumne.
Exemple complex de diccionari de diccionaris amb múltiples nivells===
A mesura que augmenta la complexitat de les dades, es poden afegir més nivells dins d’un diccionari de diccionaris. Per exemple, podríeu afegir informació sobre l’aula on estudien els alumnes
Per exemple, es pot afegir informació sobre l’aula on estudien els alumnes.
Estudieu l’exemple següent i com s’accedeix a l’aula on estudia l’alumne amb DNI 1234567A.
>>> alumnes = { ... '12345678A': { ... 'nom': 'Maria', ... 'edat': 20, ... 'notes': [6, 7, 8], ... 'aula': {'nom': 'Física', 'codi': 'A1'} ... }, ... '87654321B': { ... 'nom': 'Pere', ... 'edat': 22, ... 'notes': [5, 6, 7], ... 'aula': {'nom': 'Matemàtiques', 'codi': 'B2'} ... } ... } >>> alumnes['12345678A'] {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8], 'aula': {'nom': 'Física', 'codi': 'A1'}} >>> alumnes['12345678A']['aula']['nom'] 'Física'
Els iterables en Python
Un iterable és qualsevol objecte que es pot recórrer, element per element, en una seqüència. En Python, això vol dir que és un objecte sobre el qual es pot iterar utilitzant un bucle for, com ara les llistes, els diccionaris, les tuples i les cadenes de text.
Els iterables es poden descompondre en dos tipus d’objectes principals:
- Iterables: són objectes com les llistes o els diccionaris que contenen altres elements i poden ser recorreguts un a un.
- Iteradors: són objectes que s’utilitzen per recórrer iterables; es poden obtenir a partir d’un iterable mitjançant la funció
iter(). L’iterador proporciona els elements d’un en un mitjançant la funciónext().
Característiques dels iterables
Els iterables tenen dues propietats importants:
- Es poden recórrer amb un bucle
for. Quan feu- for element in iterable:
Python recorre cada element de l’iterable fins que ja no hi ha més elements.
- Es poden convertir en una llista. La funció
list()pot rebre com a argument un iterable i transformar-lo en una llista, la qual cosa és útil per manipular col·leccions de dades que d’altra manera no serien fàcils d’accedir. - Tots els iterables es poden ordenar amb la funció
sorted(). Aquesta funció retorna una llista ordenada, independentment del tipus d’iterable original.
Exemples de tipus de dades iterables:
- Llistes
- [1, 2, 3]
- Tuples
- (1, 2, 3)
- Cadenes de text
- "Hola"
- Diccionaris
- {'clau1': 'valor1', 'clau2': 'valor2'}
Objectes iterables relacionats amb els diccionaris
Els diccionaris són iterables especials en Python perquè tenen claus i valors associats. A més a més, alguns dels mètodes dels diccionaris proporcionen altres objectes que també són iterables.
Es pot iterar sobre:
- Un diccionari. En aquest cas la iteració es fa recorrent les claus.
- L’iterable que retorna el mètode
keys(). També s’efectua la iteració sobre les claus del diccionari. - L’iterable que retorna el mètode
values(). La iteració s’efectua sobre els valors del diccionari. - L’iterable que retorna el mètode
items(). En aquesta ocasió, la iteració s’efectua sobre les tuples de dos elements que contenen les parelles clau-valor del diccionari.
Tots aquests mètodes retornen el que s’anomenen vistes de diccionari. Són un tipus de dada diferent entre ells i diferents d’una llista, però que es poden convertir a llista amb la funció list(). Quan es treballa amb les vistes no és recomanable fer la conversió, però sí que va bé tenir en compte com seria aquest objecte convertit en llista, donat que us ajudarà a treballar amb les vistes de manera intuïtiva.
Vistes de diccionari i llistes
Fixeu-vos en el tipus d’objecte que retorna els mètodes de vista de diccionaris, i què contenen les llistes després de fer la transformació. Tenir present aquesta transformació us ajudarà a treballar amb aquests objectes.
>>> diccionari ={1:'a', 2:'b', 3:'c'} >>> diccionari.keys() dict_keys([1, 2, 3]) >>> list(diccionari.keys()) [1, 2, 3] >>> diccionari.values() dict_values(['a', 'b', 'c']) >>> list(diccionari.values()) ['a', 'b', 'c'] >>> diccionari.items() dict_items([(1, 'a'), (2, 'b'), (3, 'c')]) >>> list(diccionari.items()) [(1, 'a'), (2, 'b'), (3, 'c')]
Això facilita l’accés a les dades i permet utilitzar el diccionari com a base per a ordenacions o filtres. Per exemple, si tenim el diccionari d’estudiants alumnes, podem accedir a les claus o valors per separat:
Diferències entre el tipus de dades i els objectes de vista dels diccionaris
A continuació es mostraran les diferències principals entre els diccionaris i els objectes de vista de diccionari (dict_keys, dict_values, i dict_items) en Python.
Tipus de dades: diccionari
Un diccionari en Python és una col·lecció ordenada, segons l’ordre d’inserció, de paral·lels clau-valor que permet un accés molt eficient a les dades mitjançant les seves claus.
Els diccionaris tenen les propietats següents:
- Mutabilitat: es poden modificar els diccionaris.
- Accés directe: es pot accedir als valors a través de les seves claus.
- Representació completa: emmagatzemen directament les dades, és a dir, cada parella clau-valor és completament accessible i editable en el diccionari.
Objectes de vista de diccionari
Els objectes de vista de diccionari (dict_keys, dict_values, dict_items) són representacions especials que mostren elements específics d’un diccionari (claus, valors o parelles clau-valor), però amb algunes diferències importants respecte als diccionaris complets.
Propietats dels objectes de vista:
- Iterables, però no mutables: els objectes de vista reflecteixen els continguts del diccionari i es poden recórrer en un bucle (són iterables), però no es poden modificar directament.
- Reflexió dinàmica dels canvis: a diferència d’una còpia, aquests objectes són una vista directa del diccionari, de manera que qualsevol canvi en el diccionari original es veurà reflectit en l’objecte de vista.
- No són llistes: tot i que són iterables, no admeten operacions de modificació pròpies de les llistes, com
append()osort(). Si es volen tractar com una llista, cal convertir-los explícitament amblist().
Es poden resumir les diferències entre els diccionaris i els seus objectes vista en la taula següent taula.
| Característica | Diccionari | Objecte vista |
|---|---|---|
| Mutabilitat | Sí, és mutable | No es pot modificar directament |
| Accés directe a valors | Sí, amb clau | No, només es poden recórrer |
| Actualització en temps real | Sí, conté les dades completes | Sí, reflecteix els canvis en temps real |
Els objectes de vista de diccionari són útils quan es vol accedir als elements d’un diccionari per recórrer-los o visualitzar-los, sense voler manipular-los directament. A més, consumeixen menys memòria en comparació amb les còpies de dades completes, ja que no creen una nova estructura sinó que referencien els elements del diccionari original.
Ordenació de diccionaris
Ara que ja coneixeu els objectes iterables i les vistes dels diccionaris, veureu com ordenar les dades d’un diccionari en funció d’uns camps específics, com l’edat o la mitjana de notes. Aquesta capacitat resulta fonamental per a l’anàlisi i l’organització de dades, ja que permet obtenir la informació segons el criteri que interessi.
Repàs de les funcions lambda
Les funcions lambda són funcions petites, anònimes i d’una sola línia que es poden definir de manera ràpida en Python. Es creen amb la paraula clau lambda i són útils per passar com a funció a altres funcions, com ara sorted().
La sintaxi general d’una funció lambda és:
lambda arguments: expressió
Amb la definició següent:
- Arguments: els valors d’entrada de la funció lambda.
- Expressió: el càlcul o valor que retorna.
Exemple d'una funció ''lambda'' simple
Si l’objectiu és crear una funció que multipliqui un número per 2, aquest seria el seu codi amb lambda:
Fixeu-vos en la diferent manera d’actuar segons el tipus de dada rebut
>>> multiplica_per_dos = lambda x: x * 2 >>> multiplica_per_dos(5) 10 >>> multiplica_per_dos('5') '55'
Repàs de la funció ''sorted()''
La funció sorted() permet ordenar elements d’un iterable i retorna una llista ordenada, deixant l’original intacte. És ideal per ordenar tant llistes com altres objectes iterables, com els resultats de keys(), values() o items() en un diccionari.
La sintaxi de sorted() és:
Els arguments de la funció són els següents:
sorted(iterable, key=None, reverse=False)
iterable: l’element que volem ordenar (com una llista o un objecte de vista d’un diccionari).key(opcional): una funció (com lambda) que especifica el criteri d’ordenació, per exemple, un camp específic dins d’un diccionari o la mitjana d’una llista de números.reverse(opcional): un valor booleà que, si ésTrue, ordena els elements de manera descendent.
És molt important remarcar que actua sobre un iterable, però sempre retorna el tipus de dada list, i que el diccionari no s’altera, mantenint l’ordre original d’inserció.
Exemples d'ordenació de diccionaris segons criteris diferents
Els exemples llistats a continuació actuen tots sobre el diccionari alumnes. Si treballeu amb l’intèrpret interactiu no cal introduir-lo més d’una vegada, donat que l’ús de sorted() no l’alterarà.
alumnes = { '12345678A': {'nom': 'Maria', 'edat': 20, 'notes': [5, 7, 9]}, '87654321B': {'nom': 'Pere', 'edat': 22, 'notes': [6, 8, 10, 3]}, '11223344C': {'nom': 'Carla', 'edat': 21, 'notes': [4, 6, 5]}, }
Cada valor del diccionari alumnes conté tres camps:
- nom: el nom de l’alumne,
- edat: l’edat de l’alumne,
- notes: una llista de les notes de l’alumne.
Actuació de sorted() sobre un diccionari
Quan l’argument que es vol ordenar és un diccionari i no hi ha altres arguments, sorted() retorna la llista ordenada per l’ordre natural de les claus, que en aquest cas és el de les cadenes.
>>> sorted(alumnes) ['11223344C', '12345678A', '87654321B'] >>> sorted(alumnes.keys()) ['11223344C', '12345678A', '87654321B']
Podeu observar que si sorted() s’aplica sobre la vista keys() es produeix el mateix resultat.
Actuació de sorted() amb més arguments sobre un diccionari
L’aplicació de sorted() sobre un diccionari també permet obtenir la llista resultant ordenada segons qualsevol altre criteri que no sigui l’ordre natural, definint la funció lambda corresponent i utilitzant, si escau, l’argument reverse.
>>> sorted(alumnes.keys(), key=lambda x:x[-1], reverse = True) ['11223344C', '87654321B', '12345678A']
En aquest exemple s’ha definit una funció lambda que retorna l’últim caràcter del DNI (la lletra), i s’ha mostrat el resultat en ordre invers.
Podeu comprovar que es mostra la llista ordenada de manera inversa segons la lletra dels DNI.
Ordre segons un dels camps del diccionari secundari
Com que alumnes és un diccionari, els mètodes de vista com items() permeten treballar amb cada parell DNI: dades_alumne com un element iterable. A través de sorted() es poden ordenar aquests elements pel camp que es vulgui, especificant-ho aportant una funció lambda a través del paràmetre key.
L’objectiu d’aquest exemple és ordenar segons el camp de l’edat dels alumnes, i per a cadascun d’ells mostrar el DNI i el diccionari de dades.
Fixeu-vos en el codi i el resultat:
>>> sorted(alumnes.items(), key=lambda item: item[1]['edat']) [('12345678A', {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}), ('11223344C', {'nom': 'Carla', 'edat': 21, 'notes': [4, 6, 5]}), ('87654321B', {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]})]
Explicació del procés:
S’utilitza item[1][‘edat’] perquè item representa una tupla amb dues parts: item[0] és la clau del diccionari, que en aquest cas és el DNI de l’estudiant (per exemple, ‘12345678A’), item[1] és el valor associat a aquesta clau, que és un altre diccionari amb les dades de l’alumne (per exemple, {‘nom’: ‘Maria’, ‘edat’: 20, ‘notes’: [5, 7, 9]}).
alumnes.items(): retorna una vista de parells, que us podeu imaginar com una tupla (DNI, dades_alumne), on cada dades_alumne és un diccionari secundari. Aquí es mostra el que retorna el mètodeitems():
- >>> alumnes.items()
- dict_items([('12345678A', {'nom': 'Maria', 'edat': 20, 'notes': [6, 7, 8]}), ('11223344C', {'nom': 'Carla', 'edat': 21, 'notes': [4, 6, 5]}), ('87654321B', {'nom': 'Pere', 'edat': 22, 'notes': [5, 6, 7]})])
- Amb
- key=lambda item: item[1]['edat']
lambdaaccedeix a l’edat de cada alumne. El valoritem[1][‘edat’]representa el camp ‘edat’ del segon valor de la tupla, que és el diccionari de cada alumne. El que fa aquesta funció lambda és retornar l’edat, que és un valor enter. - Sortida:
sorted()retorna una llista de tuples (DNI, dades_alumne), ordenada pel valor que li ha proporcionat la funciólambda(l’enter edat de cada alumne), en ordre ascendent.
Ús de sorted() o sort()
Quan es treballa amb col·leccions de dades com els diccionaris, comprendre la diferència entre sorted() i sort() i saber quan utilitzar cadascuna és essencial.
Quan s’obtenen les claus, valors o elements d’un diccionari utilitzant keys(), values() o items(), el resultat és un objecte de vista de diccionari.
Aquests objectes de vista no es poden ordenar amb el mètode sort(), ja que aquest només està disponible per a les llistes. En canvi, sorted() accepta qualsevol iterable (incloent-hi objectes de vista), fet que el fa molt més versàtil per ordenar dades directament.
Però s’ha de tenir en compte un fet essencial: la funció sorted() retorna una llista. És a dir, que una vegada s’ha ordenat la vista del diccionari per algun criteri, no és necessari tornar a crear cap nova vista per aplicar de nou sorted(). És més eficient aplicar sort() sobre la llista ordenada i ordenar-la in situ sense la necessitat de crear-la novament.
Exemple d'ús en la programació estructurada
Per acabar, es veurà mitjançant un exemple, com definir i utilitzar una funció per a mostrar els resultats d’un càlcul de freqüències, mitjançant un histograma horitzontal.
L’objectiu serà afegir una nova utilitat al mòdul de diccionaris, que permetrà visualitzar els resultats d’un recompte de freqüències utilitzant un histograma horitzontal, més elegant que mostrar el diccionari directament.
A més a més, la funció permetrà rebre com a paràmetres el criteri d’ordre en el qual es mostrarà l’histograma, semblant a la manera en què es crida a la funció sorted() amb els paràmetres opcionals.
Funcionament: exemple d'execució
L’objectiu final serà fer un programa que compti les freqüències de totes les paraules llegides per argument. El resultat es mostrarà en format d’histograma horitzontal: primer es presentarà un histograma ordenat segons l’ordre lexicogràfic de les paraules introduïdes, i després un altre histograma ordenat segons la freqüència de les paraules introduïdes en ordre descendent.
$ python3 adeu_andreu.py hola hola adeu andreu dius hola però mai dius adeu histograma ordenat alfabèticament adeu : ** andreu : * dius : ** hola : *** mai : * però : * histograma ordenat segons la freqüència hola : *** adeu : ** dius : ** andreu : * però : * mai : *
Tot seguit, es faran els passos necessaris per aconseguir la codificació del programa. Pel que fa al joc de proves, es pot utilitzar com a joc de proves el mateix exemple d’execució, o sigui que es pot passar directament a descriure el refinament.
Refinament del programa
L’algorisme del programa, que concreta les passes que ha de fer el codi és el següent:
- Lectura de la llista de paraules per argument
- Creació del diccionari de freqüències buit
- Omplir el diccionari amb les freqüències de totes les paraules
- Mostrar l’histograma horitzontal ordenat segons les claus
- Mostrar l’histograma horitzontal ordenat segons les freqüències
Definició de funcions
Els passos 1 i 2 (llegir la llista i crear el diccionari) es poden implementar directament dins del programa.
Per al pas 3 (omplir el diccionari), ja teniu una funció en el mòdul diccionaris que compta la freqüència de tots els elements d’una seqüència. El docstring de la funció és el següent:
def frequencies_tot(seq, dic): ''' Compta les freqüències de tots els elements de la seqüència, i les afegeix a les del diccionari. Entrada: seqüència, diccionari (pot ser buit) Sortida: None (el diccionari es passa per referència) '''
Respecte als passos 4 i 5 (per mostrar els histogrames), caldrà definir una única funció que serà l’encarregada de dibuixar l’histograma. Aquesta funció rebrà uns paràmetres com els de la funció sorted() que s’encarregaran de determinar el criteri d’ordre de l’histograma.
Aquesta nova funció s’afegirà al mòdul de diccionaris, i tindrà els següents docstring i test-driver:
def histograma_ordenat(freq_dict, key=lambda x: x[0], reverse=False): ''' Dibuixa un histograma horitzontal respectant l'ordre indicat Entrada: diccionari compatible amb l'ordre demanat, key: paràmetre opcional per definir el criteri d'ordre. Per defecte serà l'ordre natural reverse: paràmetre opcional que indica si l'ordre és ascendent. Per defecte tindrà el valor True, que indica ordre ascendent. Cal escriure False si es vol ordenar de manera descendent. el valor per defecte), o False per fer-lo descendent. Sortida: None ''' if True: vocals = {'a':0, 'e':2, 'i':0, 'o':5, 'u':0 } paraules = { 'a': 5, 'llarg': 2, 'casa': 3} histograma_ordenat(vocals) print() histograma_ordenat(vocals, reverse=True) print() histograma_ordenat(paraules, key = lambda x: x[1], reverse = True)
Codificar les funcions
El refinament de la funció histograma_ordenat() serà:
- Crear una llista ordenada segons el criteri amb
sorted() - Generar l’histograma
I el codi de la nova funció:
def histograma_ordenat(freq_dict, key=lambda x: x[0], reverse=False): ''' Dibuixa un histograma horitzontal respectant l'ordre indicat Entrada: diccionari compatible amb l'ordre demanat, key paràmetre opcional per definir l'ordre. Per defecte l'ordre natural reverse: paràmetre opcional que indica si l'ordre és ascendent (True, el valor per defecte), o False per fer-lo descendent. Sortida: None ''' # Ordenar el diccionari amb sorted() amb els paràmetres proporcionats elements_ordenats = sorted(freq_dict.items(), key=key, reverse=reverse) # Generar l'histograma for clau, freq in elements_ordenats: # Utilitzem f-string amb espai reservat de 10 caràcters per la clau print(f"{clau:<10}: {'*' * freq}") return None
Codificar el programa
Per acabar ja només cal expandir les línies de refinament del programa, implementant cada pas de l’algoritme.
El codi resultant és:
import sys import modul_diccionaris # Lectura de la llista de paraules per argument paraules = sys.argv[1:] # Creació del diccionari de freqüències buit dic = {} # Omplir el diccionari amb les freqüències de totes les paraules modul_diccionaris.frequencies_tot(paraules, dic) print(dic) print('histograma ordenat alfabèticament') modul_diccionaris.histograma_ordenat(dic) print() print('histograma ordenat segons la freqüència') modul_diccionaris.histograma_ordenat(dic, key = lambda x: x[1], reverse = True)
El mòdul diccionaris complet que necessitareu per executar el programa és el següent:
''' Mòdul de funcions amb utilitats per a diccionaris ''' def diccionari_invers(dic): ''' Funció que crea un diccionari invers al que es passa per argument. Invers significa que si l'original és {clau:valor}, el nou serà {valor:clau} ATENCIÓ: els valors de l'original han de ser tipus de dada immutable i ha de tenir tots els valors diferents. Entrada: diccionari Sortida:diccionari ''' invers = {} for k, v in dic.items(): invers[v] = k return invers def comptar_freqs(seq, dic_elements): ''' Entrada: seqüència d'elements, diccionari de freqüències amb els elements a comptar. Sortida: None (el diccionari es passa per referència) ''' for c in seq: if c in dic_elements: dic_elements[c] +=1 return def frequencies_tot(seq, dic): ''' Compta les freqüències de tots els elements de la seqüència, i les afegeix a les del diccionari. Entrada: seqüència, diccionari (pot ser buit) Sortida: None (el diccionari es passa per referència) ''' for el in seq: if el in dic: dic[el] +=1 else: dic[el] = 1 def crear_dic_frecs(claus): ''' Crea un diccionari a partir de les claus i posa 0 a tots els valors Entrada: seqüència d'elements immutables Sortida: diccionari ''' dic = {} for c in claus: dic[c] = 0 return dic def histograma_ordenat(freq_dict, key=lambda x: x[0], reverse=False): ''' Dibuixa un histograma horitzontal respectant l'ordre indicat Entrada: diccionari compatible amb l'ordre demanat, key paràmetre opcional per definir l'ordre. Per defecte l'ordre natural reverse: paràmetre opcional que indica si l'ordre és ascendent (True, el valor per defecte), o False per fer-lo descendent. Sortida: None ''' # Ordenem el diccionari amb sorted() amb els paràmetres proporcionats elements_ordenats = sorted(freq_dict.items(), key=key, reverse=reverse) # Generem l'histograma for clau, freq in elements_ordenats: # Utilitzem f-string amb espai reservat de 10 caràcters per la clau print(f"{clau:<10}: {'*' * freq}") return None if __name__ == "__main__": # test-drivers if False: print(diccionari_invers({ 1: "gener", 2: "febrer", 3: "març", 4: "abril", 5: "maig", 6: "juny", 7: "juliol", 8: "agost", 9: "setembre", 10: "octubre", 11: "novembre", 12: "desembre" })) if False: vocals = {'a':0, 'e':0, 'i':0, 'o':0, 'u':0 } comptar_freqs('hola', vocals) print(vocals) dic_els = {'t':0, 'u':0, 'X': 1} comptar_freqs('XX què passa aquí?', dic_els) print(dic_els) paraules = {'hola':0, 'adeu':0, 'Andreu':0} comptar_freqs(['hola', 'adeu'], paraules) print(paraules) if False: d = {} frequencies_tot('quin bon dia que fa', d) print(d) frequencies_tot('qqqqqqqqqqqq', d) print(d) if False: print(crear_dic_frecs('hola')) print(crear_dic_frecs(['hola', 'hola', 'adeu']))



