next up previous contents
Siguiente: La venganza de las hileras Arriba: Tutor de Python para No-programadores Anterior: Usando Modulos   Contenido

Más acerca de Listas

Ya hemos visto listas y cómo podemos usarlas.  Ahora que ya tiene más fundamentos puedo ahondar en las listas.  Primero, veremos más modos de obtener los elementos en una lista y luego hablaremos acerca de cómo copiarlos.

He aquí algunos ejemplos del uso de índices para obtener un sólo elemento de una lista:

>>> lista = ['cero','uno','dos','tres','cuatro','cinco']   
>>> lista[0]
'cero'
>>> lista[4]
'cuatro'
>>> lista[5]
'cinco'
Todos estos ejemplos debieran resultarle familiares.  Si quiere el primer elemento en la lista vea al índice 0.  El segundo elemento es índice 1 y así para toda la lista.  Sin embargo, ¿qué tal si desea el último elemento de la lista?  Un modo pudiera ser usar la función len como lista[len(lista)-1].  Este modo funciona porque la función len siempre devuelve el último índice más uno.  El penúltimo elemento sería lista[len(lista)-2].  Hay un modo más fácil.  En Python el último índice es siempre el índice -1.  El penúltimo elemento es índice -2 y así sucesivamente.  Vea algunos ejemplos:
>>> lista[len(lista)-1]  
'cinco'
>>> lista[len(lista)-2]
'cuatro'
>>> lista[-1]
'cinco'
>>> lista[-2]
'cuatro'
>>> list[-6]
'cero'
Por tanto, cualquier elemento de la lista puede ser indexado de dos modos: desde el principio o desde el final.

Otro modo útil de obtener pedazos de listas es usando rebanadas.  Veamos otro ejemplo para darle una idea de para qué son útiles:

>>> lista = [0,'Pedro',2,'S.P.A.M.','Media',42,"Juan","Juana"]
>>> lista[0]
0
>>> lista[7]
'Juana'
>>> lista[0:8]
[0,'Pedro',2,'S.P.A.M.','Media',42,"Juan","Juana"]
>>> lista[2:4]
[2, 'S.P.A.M.']
>>> lista[4:7]
['Media', 42, 'Juan']
>>> lista[1:5]
['Pedro', 2, 'S.P.A.M.', 'Media']
Las rebanadas sirven para devolver parte de una lista.  El operador de rebanado tiene la forma lista[primer_indice:siguiente_indice].  La rebanada va desde el primer_indice al índice anterior al siguiente_indice. Puede usar ambos tipos de indexado:
>>> lista[-4:-2]
['Media', 42]
>>> list[-4]
'Media'
>>> list[-4:6]
['Media', 42]
Otro truco con rebanadas es el índice tácito.  Si el primer índice no es especificado, Python supone que quiere desde el principio de la lista.  Si el siguiente indice no es especificado, Python supone que quiere todo el resto de la lista.  Algunos ejemplos:
>>> lista[:2]
[0, 'Pedro']
>>> lista[-2:]
['Juan', 'Juana']
>>> lista[:3]
[0, 'Pedro', 2]
>>> lista[:-5]
[0, 'Pedro', 2]
Vea un programa de ejemplo (copie y pegue la definición del poema, si así lo prefiere):
poema = ["<B>","Dona","Blanca","</B>","esta","cubierta","de","pilares","de","<B>",\
"oro","y","plata","</B>","romperemos","un","pilar", "para', "ver", "a", "<B>","Dona",\
"Blanca","</B>"]

def sacar_negritas(lista):
verdadero = 1
falso = 0
## es_negrita dice si estamos viendo a una sección en negrita del texto
es_negrita = falso
## comienza_bloque es el indice del comienzo ya sea de un fragmento
## normal o un fragmento en negrita del texto
comienza_bloque = 0
for indice in range(len(lista)):
##Maneja comienzo de texto negrita
if lista[indice] == "<B>":
if es_negrita:
print "Error: Extra comienzo de negrita"
##print "No negrita:",list[comienzo_bloque:indice]
es_negrita = verdadero
comienzo_bloque = indice+1
##Maneja final de texto negrita
if lista[indice] == "</B>":
if not es_negrita:
print "Error: Extra cierre de negrita"
print "Negrita [",comienzo_bloque,":",indice,"] ",\
lista[comienzo_bloque:indice]
es_negrita = falso
comienzo_bloque = indice+1

sacar_negritas(poema)
y la salida es:
Negrita [ 1 : 3 ]  ['Dona', 'Blanca']
Negrita [ 10 : 13 ] ['oro', 'y', 'plata']
Negrita [ 21 : 23 ] ['Dona', 'Blanca']

La función sacar_negritas toma una lista donde cada elemento es una palabra o un símbolo.  Los símbolos que busca con <B> el cual comienza texto en negrita y <\B> el cual termina texto en negrita.  La función sacar_negritas recorre la lista y busca símbolos de comienzo y final.

El siguiente aspecto de las listas es copiarlas.  Si trata algo sencillo como:

>>> a = [1,2,3]
>>> b = a
>>> print b
[1, 2, 3]
>>> b[1] = 10
>>> print b
[1, 10, 3]
>>> print a
[1, 10, 3]
Esto probablemente resulte desconcertante, ya que una modificación a bresultó en que también a cambiara.  Lo que pasó es que la asignación b = a hace a b una referencia a a.  Esto significa que podemos considerar a b como otro nombre para a.  Por lo tanto, cualquier modificación a  b también afecta a a.  Sin embargo, algunas asignaciones no crean dos nombres para una lista:
>>> a = [1,2,3]
>>> b = a*2
>>> print a
[1, 2, 3]
>>> print b
[1, 2, 3, 1, 2, 3]
>>> a[1] = 10
>>> print a
[1, 10, 3]
>>> print b
[1, 2, 3, 1, 2, 3]

En este caso b no es una referencia a a porque la expresión a*2 crea una lista nueva.  Luego el enunciadob = a*2 da a b una referencia a a*2 más que una referencia a a.  Todas las operaciones de asignación crean una referencia.  Cuando usted pasa una lista como argumento de una función, también crea una referencia.  La mayor parte del tiempo no tiene que preocuparse por crear referencias y no copias.  Sin embargo, cuando necesite modificar una lista sin afectar a otra tiene que asegurarse que realmente haya creado una copia.

Hay varios modos para hacer una copia de una lista.  El más simple, que funciona casi todo el tiempo es el operador rebanador ya que siempre hace una nueva lista aún cuando la rebanada abarca toda la lista:

>>> a = [1,2,3]
>>> b = a[:]
>>> b[1] = 10
>>> print a
[1, 2, 3]
>>> print b
[1, 10, 3]

Tomar la rebanada [:] crea una nueva copia de la lista.  Sin embargo, solamente copia la lista más externa.  Cualquier sublista contenida será una referencia a la sublista en la lista original.  Por tanto, cuando la lista contiene otras listas como elementos, necesitamos copiar también las listas más internas.  Puede hacerlo manualmente, pero Python contiene un módulo que lo hace.  Use la función deepcopy del módulo copy :

>>> import copy
>>> a = [[1,2,3],[4,5,6]]
>>> b = a[:]
>>> c = copy.deepcopy(a)
>>> b[0][1] = 10
>>> c[1][1] = 12
>>> print a
[[1, 10, 3], [4, 5, 6]]
>>> print b
[[1, 10, 3], [4, 5, 6]]
>>> print c
[[1, 2, 3], [4, 12, 6]]
Primero que nada, note que a es un arreglo de arreglos (o lista de listas).  Luego observe que cuando ejecutamos b[0][1] = 10 tanto a como b cambian, pero c no.  Esto es debido a que las listas más internas son referencias cuando hacemos rebanadas, pero condeepcopy c fue copiado totalmente.

Entonces, ¿debería preocuparme acerda de referencias cada vez que uso una función o una asignación?  Las buenas noticias son que sólo tiene que preocuparse de las referencias cuando usa diccionarios o listas.  Los números y las hileras crean referencias al momento de asignación, pero cada operación que modifica números e hileras crea una nueva copia de modo que no puede modificarlos inadvertidamente.  Sí debe pensar acerca de referencias cuando modifique una lista o un diccionario.

En este momento quizá esté preguntándose porqué existen las referencias.  La razón básica es velocidad.  Es mucho más rápido hacer una referencia a una lista de mil elementos que copiar todos los elementos.  La otra razón es que permite que una función modifique la lista o diccionario que recibe como parámetro.  Sólo recuerde que las referencias existen si llega a tener algún problema extraño de datos que cambian cuando no debieran.


next up previous contents
Siguiente: La venganza de las hileras Arriba: Tutor de Python para No-programadores Anterior: Usando Modulos   Contenido
Josh Cogliati jjc@honors.montana.edu
Traducido por Victor M. Rosas-Garcia quimico69@yahoo.com