![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Los tipos compuestos que ha visto hasta ahora (cadenas, listas y tuplas) usan enteros como �ndices. Si intenta usar cualquier otro tipo como �ndice provocar� un error.
Los diccionarios son similares a otros tipos compuestos excepto en que pueden usar como �ndice cualquier tipo inmutable. A modo de ejemplo, crearemos un diccionario que traduzca palabras inglesas al espa�ol. En este diccionario, los �ndices son strings (cadenas).
Una forma de crear un diccionario es empezar con el diccionario vac�o y a�adir elementos. El diccionario vac�o se expresa como {}:
>>> ing\_a\_esp = {}
>>> ing\_a\_esp['one'] = 'uno'
>>> ing\_a\_esp['two'] = 'dos'
La primera asignaci�n crea un diccionario llamado ing_a_esp; las otras asignaciones a�aden nuevos elementos al diccionario. Podemos presentar el valor actual del diccionario del modo habitual:
>>> print ing\_a\_esp
{'one': 'uno', 'two': 'dos'}
Los elementos de un diccionario aparecen en una lista separada por comas. Cada entrada contiene un �ndice y un valor separado por dos puntos (:). En un diccionario, los �ndices se llaman claves, por eso los elementos se llaman pares clave-valor.
Otra forma de crear un diccionario es dando una lista de pares clave-valor con la misma sintaxis que la salida del ejemplo anterior:
>>> ing\_a\_esp = {'one': 'uno', 'two': 'dos', 'three': 'tres'}
Si volvemos a imprimir el valor de ing_a_esp, nos llevamos una sorpresa:
>>> print ing\_a\_esp
{'one': 'uno', 'three': 'tres', 'two': 'dos'}
�Los pares clave-valor no est�n en orden! Afortunadamente, no necesitamos preocuparnos por el orden, ya que los elementos de un diccionario nunca se indexan con �ndices enteros. En lugar de eso, usamos las claves para buscar los valores correspondientes:
>>> print ing\_a\_esp['two']
'dos'
La clave 'two' nos da el valor 'dos' aunque aparezca en el tercer par clave-valor.
La sentencia del elimina un par clave-valor de un diccionario. Por ejemplo, el diccionario siguiente contiene los nombres de varias frutas y el n�mero de esas frutas en el almac�n:
>>> inventario = {'manzanas': 430, 'bananas': 312,
... 'naranjas': 525, 'peras': 217}
>>> print inventario
{'naranjas': 525, 'manzanas': 430, 'peras': 217, 'bananas': 312}
Si alguien compra todas las peras, podemos eliminar la entrada del diccionario:
>>> del inventario['peras']
>>> print inventario
{'naranjas': 525, 'manzanas': 430, 'bananas': 312}
O si esperamos recibir m�s peras pronto, podemos simplemente cambiar el inventario asociado con las peras:
>>> inventario['peras'] = 0
>>> print inventario
{'naranajas': 525, 'manzanas': 430, 'peras': 0, 'bananas': 312}
La funci�n len tambi�n funciona con diccionarios; devuelve el n�mero de pares clave-valor:
>>> len(inventario)
4
Un m�todo es similar a una funci�n, acepta par�metros y devuelve un valor, pero la sintaxis es diferente. Por ejemplo, el m�todo keys acepta un diccionario y devuelve una lista con las claves que aparecen, pero en lugar de la sintaxis de la funci�n keys(ing_a_esp), usamos la sintaxis del m�todo ing_a_esp.keys().
>>> ing\_a\_esp.keys()
['one', 'three', 'two']
Esta forma de notaci�n de punto especifica el nombre de la funci�n, keys, y el nombre del objeto al que se va a aplicar la funci�n, ing_a_esp. Los par�ntesis indican que este m�todo no admite par�metros.
La llamda a un m�todo se denomina invocaci�n; en este caso, dir�amos que estamos invocando keys sobre el objeto ing_a_esp.
El m�todo values es similar; devuelve una lista de los valores del diccionario:
>>> ing\_a\_esp.values()
['uno', 'tres', 'dos']
El m�todo items devuelve ambos, una lista de tuplas con los pares clave-valor del diccionario:
>>> ing\_a\_esp.items()
[('one','uno'), ('three', 'tres'), ('two', 'dos')]
La sintaxis nos proporciona informaci�n muy �til acerca del tipo de datos. Los corchetes indican que es una lista. Los par�ntesis indican que los elementos de la lista son tuplas.
Si un m�todo acepta un argumento, usa la misma sintaxis que una llamada a una funci�n. Por ejemplo, el m�todo has_key acepta una clave y devuelve verdadero (1) si la clave aparece en el diccionario:
>>> ing\_a\_esp.has_key('one')
1
>>> ing\_a\_esp.has_key('deux')
0
Si usted invoca un m�todo sin especificar un objeto, provoca un error. En este caso, el mensaje de error no es de mucha ayuda:
>>> has_key('one')
NameError: has_key
Debe usted estar atento a los alias a causa de la mutabilidad de los diccionarios. Si dos variables se refieren al mismo objeto los cambios en una afectan a la otra.
Si quiere modificar un diccionario y mantener una copia del original, use el m�todo copy. Por ejemplo, opuestos es un diccionario que contiene pares de opuestos:
>>> opuestos = {'arriba': 'abajo', 'derecho': 'torcido',
... 'verdadero': 'falso'}
>>> alias = opuestos
>>> copia = opuestos.copy()
alias y opuestos se refieren al mismo objeto; copia hace referencia a una copia nueva del mismo diccionario. Si modificamos alias, opuestos tambi�n resulta cambiado:
>>> alias['derecho'] = 'sentado'
>>> opuestos['derecho']
'sentado'
Si modificamos copia, opuestos no var�a:
>>> copia['derecho'] = 'privilegio'
>>> opuestos['derecho']
'sentado'
En la Secci�n usamos una lista de listas para representar una matriz. Es una buena opci�n para una matriz en la que la mayor�a de los valores es diferente de cero, pero piense en una matriz como �sta:
La representaci�n de la lista contiene un mont�n de ceros:
matriz = [ [0,0,0,1,0],
[0,0,0,0,0],
[0,2,0,0,0],
[0,0,0,0,0],
[0,0,0,3,0] ]
Una posible alternativa es usar un diccionario. Como claves, podemos usar tuplas que contengan los n�meros de fila y columna. �sta es la representaci�n de la misma matriz por medio de un diccionario:
matriz = {(0,3): 1, (2, 1): 2, (4, 3): 3}
S�lo hay tres pares clave-valor, una para cada elemento de la matriz diferente de cero. Cada clave es una tupla, y cada valor es un entero.
Para acceder a un elemento de la matriz, podemos usar el operador []:
matriz[0,3]
1
F�jese en que la sintaxis para la representaci�n por medio del diccionario no es la misma de la representaci�n por medio de la lista anidada. En lugar de dos �ndices enteros, usamos un �ndice que es una tupla de enteros.
Hay un porblema. Si apuntamos a un elemento que es cero, se produce un error porque en el diccionario no hay una entrada con esa clave:
>>> matriz[1,3]
KeyError: (1, 3)
El m�todo get soluciona este problema:
>>> matriz.get((0,3), 0)
1
El primer argumento es la clave; el segundo argumento es el valor que debe devolver get en caso de que la clave no est� en el diccionario:
>>> matriz.get((1,3), 0)
0
get mejora sensiblemente la sem�ntica del acceso a una matriz dispersa. L�stima de sintaxis.
Si estuvo jugando con la funci�n fibonacci de la Secci�n 5.7, es posible que haya notado que cuanto m�s grande es el argumento que le da, m�s tiempo le cuesta ejecutarse. M�s a�n, el tiempo de ejecuci�n aumenta muy r�pidamente. En nuestra m�quina, fibonacci(20) acaba instant�neamente, fibonacci(30) tarda m�s o menos un segundo, y fibonacci(40) tarda una eternidad.
Para entender por qu�, observe este gr�fico de llamadas de fibonacci con n=4:
Un gr�fico de llamadas muestra un conjunto de cajas de funci�n con l�neas que conectan cada caja con las cajas de las funciones a las que llama. En lo alto del gr�fico, fibonacci con n=4 llama a fibonacci con n=3 y n=2. A su vez, fibonacci con n=3 llama a fibonacci con n=2 y n=1. Y as� sucesivamente.
Cuente cu�ntas veces se llama a fibonacci(0) y fibonacci(1). Es una soluci�n ineficaz al problema, y empeora mucho tal como crece el argumento.
Una buena soluci�n es llevar un registro de los valores que ya se han calculado almacen�ndolos en un diccionario. A un valor que ya ha sido calculado y almacenado para un uso posterior se le llama pista. Aqu� hay una implementaci�n de fibonacci con pistas:
anteriores = {0:1, 1:1}
def fibonacci(n):
if anteriores.has_key(n):
return anteriores[n]
else:
nuevoValor = fibonacci(n-1) + fibonacci(n-2)
anteriores[n] = nuevoValor
return nuevoValor
El diccionario llamado anteriores mantiene un registro de los valores de Fibonacci que ya conocemos. El programa comienza con s�lo dos pares: 0 corresponde a 1 y 1 corresponde a 1.
Siempre que se llama a fibonacci comprueba si el diccionario contiene el resultado ya calculado. Si est� ah�, la funci�n puede devolver el valor inmediatamente sin hacer m�s llamadas recursivas. Si no, tiene que calcular el nuevo valor. El nuevo valor se a�ade al diccionario antes de que la funci�n vuelva.
Con esta versi�n de fibonacci, nuestra m�quina puede calcular fibonacci(40) en un abrir y cerrar de ojos. Pero cuando intentamos calcular fibonacci(50), nos encontramos con otro problema:
>>> fibonacci(50)
OverflowError: integer addition
La respuesta, como ver� en un momento, es 20.365.011.074. El problema es que este n�mero es demasiado grande para caber en un entero de Python. Se desborda. Afortunadamente, hay una soluci�n f�cil para este problema.
Python proporciona un tipo llamado long int que puede manejar enteros de cualquier tama�o. Hay dos formas de crear un valor long int. Una es escribir un entero con una L may�scula al final:
>>> type(1L)
<type 'long int'>
La otra es usar la funci�n long para convertir un valor en long
int. long acepta cualquier tipo num�rico e incluso cadenas de d�gitos:
>>> long(1)
1L
>>> long(3.9)
3L
>>> long('57')
57L
Todas las operaciones matem�ticas funcionan sobre los long ints, as� que no tenemos que hacer mucho para adaptar fibonacci:
>>> previous = {0:1L, 1:1L}
>>> fibonacci(50)
20365011074L
Simplemente cambiando el contenido inicial de anteriores cambiamos el comportamiento de fibonacci. Los primeros dos n�meros de la secuencia son long ints, as� que todos los n�meros subsiguientes lo ser�n tambi�n.
Como ejercicio, modifique factorial de forma que produzca un long int como resultado.
En el cap�tulo 7 escribimos una funci�n que contaba el n�mero de apariciones de una letra en una cadena. Una versi�n m�s gen�rica de este problema es crear un histograma de las letras de la cadena, o sea, cu�ntas veces aparece cada letra.
Ese histograma podr�a ser �til para comprimir un archivo de texto. Como las diferentes letras aparecen con frecuencias distintas, podemos comprimir un archivo usando c�digos cortos para las letras m�s habituales y c�digos m�s largos para las que aparecen con menor frecuencia.
Los diccionarios facilitan una forma elegante de generar un histograma:
>>> cuentaLetras = {}
>>> for letra in "Mississippi":
... cuentaLetras[letra] = cuentaLetras.get (letra, 0) + 1
...
>>> cuentaLetras
{'M': 1, 's': 4, 'p': 2, 'i': 4}
>>>
Inicialmente, tenemos un diccionario vac�o. Para cada letra de la cadena, buscamos el recuento actual (posiblemente cero) y lo incrementamos. Al final, el diccionario contiene pares de letras y sus frecuencias.
Puede ser m�s atractivo mostrar el histograma en orden alfab�tico. Podemos hacerlo con los m�todos items y sort:
>>> itemsLetras = cuentaLetras.items()
>>> itemsLetras.sort()
>>> print itemsLetras
[('M', 1), ('i', 4), ('p', 2), ('s', 4)]
Ya hab�a visto usted el m�todo items, pero sort es el primer m�todo aplicable a listas que hemos visto. Hay varios m�s, como append, extend, y reverse. Consulte la documentaci�n de Python para ver los detalles.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |