2.3: Colecciones y Looping- Listas y para
- Page ID
- 55082
Una lista, como su nombre lo indica, es una lista de datos (enteros, flotantes, cadenas, booleanos, o incluso otras listas o tipos de datos más complicados). Las listas de Python son similares a matrices o vectores en otros lenguajes. Al igual que las letras en cadenas, los elementos de una lista se indexan a partir de 0
usando la sintaxis []
. También podemos usar corchetes para crear una lista con algunos elementos de diferentes tipos, aunque en la práctica no lo haremos a menudo.
Al igual que con las cadenas, podemos usar la notación []
para obtener una sublista “slice”, y podemos usar la función len ()
para obtener la longitud de una lista.
Sin embargo, a diferencia de las cadenas, las listas son mutables, lo que significa que podemos modificarlas después de haber sido creadas, por ejemplo, reemplazando un elemento por otro elemento. Como se mencionó anteriormente, ¡las listas pueden incluso contener otras listas!
Normalmente queremos que nuestro código cree una lista vacía, y luego agregarle elementos de datos un elemento a la vez. Se devuelve una lista vacía llamando a la función list ()
sin parámetros. Dada una variable que hace referencia a un objeto list, podemos anexar un elemento al final usando el método.append ()
, dando al método el elemento que queremos anexar como parámetro.
Esta sintaxis puede parecer un poco extraña en comparación con lo que hemos visto hasta ahora. Aquí new_list.append (“G”)
le está diciendo al objeto list a la variable new_list
que se refiere para ejecutar su método.append ()
, tomando como parámetro la cadena “G”
. Exploraremos los conceptos de objetos y métodos de manera más formal en capítulos posteriores. Por ahora, consideremos la lista no sólo una recopilación de datos, sino un objeto “inteligente” con el que podemos interactuar usando.
métodos.
Tenga en cuenta que el método.append ()
pide a la lista que se modifique a sí misma (lo que puede hacer, porque las listas son mutables), pero esta operación no devuelve nada de uso. [1]
Este tipo de comando abre la posibilidad de algunos errores insidiosos; por ejemplo, una línea como new_list = new_list.append (“C”)
parece bastante inocente y no causa ningún error inmediato, pero probablemente no sea lo que pretendía el programador. El motivo es que la llamada new_list.append (“C”)
solicita satisfactoriamente a la lista que se modifique, pero luego se devuelve el valor None
, que se asignaría a la variable new_list
con la asignación. Al final de la línea, new_list
se referirá a Ninguno
, y la lista en sí ya no será accesible. (De hecho, será basura recolectada a su debido tiempo.) En resumen, use some_list.append (el)
, no some_list = some_list.append (el)
.
A menudo queremos ordenar listas, lo que podemos hacer de dos maneras. Primero, podríamos usar la función sort ()
, que toma una lista como parámetro y devuelve una nueva copia de la lista en orden ordenado, dejando solo el original. Alternativamente, podríamos llamar a un método lists .sort ()
para pedirle a una lista que se ordene en su lugar.
Al igual que con el método .append ()
anterior, el método .sort ()
devuelve None
, por lo que lo siguiente seguramente habría resultado en un error: a_list = a_list.sort ()
.
En este punto, uno sería perdonado por pensar eso.
siempre devuelven None
y así la asignación basada en los resultados no es útil. Pero antes de pasar de las listas, introduzcamos una forma sencilla de dividir una cadena en una lista de subcadenas, usando el método .split ()
en un tipo de datos de cadena. Por ejemplo, dividamos una cadena donde quiera que ocurra la subsecuencia “TA”
.
Si la secuencia fuera en cambio “CGCGTATACAGA”
, la lista resultante habría contenido ["CGCG”, “”, “CAGA"]
(es decir, uno de los elementos sería una cadena vacía de longitud cero). Este ejemplo ilustra que las cadenas, al igual que las listas, también son objetos “inteligentes” con los que podemos interactuar usando.
métodos. (De hecho, también lo son los enteros, los flotadores y todos los demás tipos de Python que cubriremos).
Tuplas (Listas inmutables)
Como se señaló anteriormente, las listas son mutables, lo que significa que pueden ser alteradas después de su creación. En algunos casos especiales, es útil crear una versión inmutable de una lista, llamada “tupla” en Python. Al igual que las listas, las tuplas se pueden crear de dos maneras: con la función tupla ()
(que devuelve una tupla vacía) o directamente.
Las tuplas funcionan como listas; podemos llamar len ()
en ellas y extraer elementos o rebanadas con sintaxis []
. No podemos cambiar, eliminar o insertar elementos. [2]
Bucle con para
Un for-loop en Python ejecuta un bloque de código, una vez por cada elemento de un tipo de datos iterable: uno al que se puede acceder un elemento a la vez, en orden. Resulta que tanto cadenas como listas son tales tipos iterables en Python, aunque por ahora exploraremos solo iterar sobre listas con bucles for.
Un bloque es un conjunto de líneas de código que se agrupan como una unidad; en muchos casos también se ejecutan como una unidad, quizás más de una vez. Los bloques en Python se indican al estar sangrados un nivel adicional (generalmente con cuatro espacios, recuerde que es consistente con esta práctica de sangría).
Cuando se usa un for-loop para iterar sobre una lista, necesitamos especificar un nombre de variable que haga referencia a cada elemento de la lista a su vez.
En lo anterior, una línea tiene sangría y un nivel adicional justo debajo de la línea que define el bucle for. En el bucle for, la variable gene_id
se establece para hacer referencia a cada elemento de la lista gene_ids
a su vez. Aquí está la salida del bucle:
El uso de bucles for en Python a menudo confunde a los principiantes, porque se está asignando una variable (por ejemplo, gen_id
) sin usar el operador de asignación standard =
. Si ayuda, puedes pensar en el primer bucle a través del bloque como ejecutar gene_id = gene_ids [0]
, la próxima vez como ejecutar gene_id = gene_ids [1]
, y así sucesivamente, hasta que se hayan utilizado todos los elementos de gene_ids
.
Los bloques pueden contener varias líneas (incluidas las líneas en blanco) para que varias líneas de código puedan funcionar juntas. Aquí hay un bucle modificado que mantiene una variable de contador
, incrementándola en una cada vez.
La salida de este bucle sería la misma que la salida anterior, con una línea adicional de impresión 3
(el contenido del contador
después de que termine el bucle).
Algunos errores comunes al usar estructuras de bloques en Python incluyen los siguientes, muchos de los cuales resultarán en un IndentationError
.
- No usar el mismo número de espacios para cada nivel de sangría, o mezclar sangría de pestaña con sangría de múltiples espacios. (La mayoría de los programadores de Python prefieren usar cuatro espacios por nivel).
- Olvidar el colon
:
eso termina la línea antes del bloque. - Usar algo así como una línea for-loop que requiere un bloque, pero no sangrar la siguiente línea.
- Innecesariamente sangría (creación de un bloque) sin una línea de definición de bucle correspondiente.
A menudo queremos recorrer un rango de enteros. Convenientemente, la función range ()
devuelve una lista de números. [3] Comúnmente toma dos parámetros: (1) el entero inicial (inclusive) y (2) el entero final (exclusivo). Así podríamos programar nuestro for-loop de manera ligeramente diferente generando una lista de enteros para usar como índices, e iterando sobre eso:
La salida de uno de los bucles anteriores:
El segundo ejemplo anterior ilustra la justificación detrás de la naturaleza inclusivo/exclusiva de la función range ()
: debido a que los índices comienzan en cero y van a uno menos que la longitud de la lista, podemos usar range (0, len (ids))
(en lugar de necesitar modificar el índice final) para correctamente iterar sobre los índices de ids
sin conocer primero la longitud de la lista. Los programadores experimentados generalmente encuentran esto intuitivo, pero aquellos que no están acostumbrados a contar desde cero pueden necesitar algo de práctica. Debes estudiar estos ejemplos de bucle cuidadosamente, y probarlos. Estos conceptos suelen ser más difíciles para los principiantes, pero son importantes de aprender.
Los bucles y los bloques que controlan pueden anidarse, con un efecto poderoso:
En lo anterior, el bucle for externo controla un bloque de cinco líneas; contenido dentro está el bucle for interno que controla un bloque de solo dos líneas. El bloque exterior se refiere principalmente a la variable i
, mientras que el bloque interno se refiere principalmente a la variable j
. Vemos que ambos bloques también hacen uso de variables definidas fuera de ellos; el bloque interno hace uso de sum
, i
, y j
, mientras que las líneas específicas del bloque externo hacen uso de sum
e i
(pero no j
). Este es un patrón común que veremos con más frecuencia. ¿Se puede determinar el valor del total
al final sin ejecutar el código?
Comprensiones de listas
Python y algunos otros lenguajes incluyen sintaxis taquigráfica especializada para crear listas a partir de otras listas conocidas como comprensiones de listas. Efectivamente, esta taquigrafía combina una sintaxis for-loop y una sintaxis de creación de listas en una sola línea.
Aquí hay un ejemplo rápido: comenzando con una lista de números [1, 2, 3, 4, 5, 6]
, generamos una lista de cuadrados ([1, 4, 9, 16, 25, 36]
):
Aquí estamos usando una convención de nomenclatura de num en nums
, pero como un for-loop, la variable loop puede ser nombrada casi cualquier cosa; por ejemplo, squares = [x ** 2 for x in nums]
lograría la misma tarea.
Las comprensiones de listas pueden ser bastante flexibles y usarse de manera creativa. Dada una lista de secuencias, podemos generar fácilmente una lista de longitudes.
Estas estructuras también admiten la “inclusión condicional”, aunque aún no hemos cubierto operadores como ==
:
El siguiente ejemplo genera una lista de 1
s por cada elemento donde la primera base es “T”
, y luego usa la función sum ()
para resumir la lista, dando como resultado un recuento de secuencias que comienzan con “T”
.
Aunque muchos programadores de Python suelen usar comprensiones de listas, no las usaremos mucho en este libro. En parte, esto se debe a que son una característica que muchos lenguajes de programación no tienen, pero también porque pueden llegar a ser difíciles de leer y entender debido a su compacidad. Como ejemplo, ¿qué opinas que hace la siguiente comprensión? [x para x en rango (2, n) si x no en [j para i en rango (2, sqrtn) para j en rango (i*2, n, i)]]
(Supongamos que n = 100
y sqrtn = 10
. Este ejemplo también hace uso del hecho de que range ()
puede tomar un argumento step, como en range (start, stop, step)
.)
Ejercicios
- ¿Cuál es el valor del
total
al final de cada bucle establecido a continuación? Primero, vea si puede calcular las respuestas a mano, y luego escribir y ejecutar el código con algunas declaracionesprint ()
agregadas para verificar sus respuestas. - Supongamos que decimos que el primer bloque for-loop anterior tiene “profundidad” 1 y “ancho” 4, y el segundo tiene profundidad 2 y ancho 4, y el tercero tiene profundidad 3 y ancho 4. ¿Se puede definir una ecuación que indique cuál sería el
total
para un bloque de bucle anidado con profundidad d y ancho w? ¿Cómo se relaciona esta ecuación con el número de veces que tiene el intérprete para ejecutar la líneatotal = total + 1
? - Determinar una ecuación que relacione el valor final del
total
por debajo con el valor dex
. - Dada una declaración de una cadena de secuencia, como
seq = “ATGATAGAGGGATACGGATAG”
, y una subsecuencia de interés, comosubseq = “GATA”
, escribir algún código que imprima todas las ubicaciones de esa subcadena en la secuencia, una por línea, usando solo los conceptos de Python que hemos cubierto hasta ahora ( comolen ()
, for-loops y.split ()
). Para el ejemplo anterior, la salida debe ser3
,11
y18
.Tu código aún debería funcionar si la subcadena ocurre al inicio o al final de la secuencia, o si la subsecuencia ocurre espalda con espalda (por ejemplo, en
“GATACCGATATAGATA”
,“GATA”
ocurre en las posiciones 1, 7 y 11). Como indicio, puede asumir que la subsecuencia no se superpone (por ejemplo, no necesita preocuparse por ubicar“GAGA”
en“GAGAGAGAGA”
, lo que ocurriría en las posiciones 1, 3, 5 y 7). - Supongamos que tenemos una matriz representada como una lista de columnas:
cols = [[10, 20, 30, 40], [5, 6, 7, 8], [0.9, 0.10, 0.11, 0.12]]
. Debido a que cada columna es una lista interna, se dice que esta disposición está en “orden de columna mayor”. Escribe algún código que produzca los mismos datos en “orden fila-mayor”; por ejemplo,las filas
deben contener[[10, 5, 0.9], [20, 6, 0.10], [30, 7, 0.11], [40, 8, 0.12]]
. Se puede suponer que todas las columnas tienen el mismo número de elementos y que la matriz es de al menos 2 por 2.Este problema es un poco complicado, pero te ayudará a organizar tus pensamientos alrededor de bucles y listas. Puede comenzar determinando primero el número de filas en los datos y luego construyendo la “estructura” de
filas
como una lista de listas vacías.
- Devuelve un tipo de datos especial conocido como
Ninguno
, que permite que exista una variable pero no haga referencia a ningún dato. (Técnicamente,Ninguno
es un tipo de datos, aunque muy simple).Ninguno
se puede usar como un tipo de marcador de posición, y así en algunas situaciones no es del todo inútil. - Las tuplas son causa de una de las partes más confusas de Python, porque se crean encerrando una lista de elementos dentro de paréntesis, pero las llamadas a funciones también toman parámetros listados dentro de paréntesis, ¡y las expresiones matemáticas también se agrupan por paréntesis! Considera la expresión
(4 + 3) * 2
. ¿Es(4 + 3)
un número entero o una tupla de un solo elemento? En realidad, es un entero. Por defecto, Python busca una coma para determinar si se debe crear una tupla, por lo que(4 + 3)
es un entero, mientras que(4 + 3, 8)
es una tupla de dos elementos y(4 + 3,)
es una tupla de un solo elemento. Use paréntesis deliberadamente en Python: ya sea para agrupar expresiones matemáticas, crear tuplas o llamar a funciones, donde el nombre de la función y el paréntesis de apertura son vecinos, como enprint (a)
en lugar deprint (a)
. Agregar innecesariamente paréntesis (y por lo tanto crear tuplas accidentalmente) ha sido la causa de algunos errores difíciles de encontrar. - Una alternativa a
range ()
en Python 2.7 esxrange ()
, que produce un tipo iterable que funciona como una lista de números pero que es más eficiente en la memoria. En versiones más recientes de Python (3.0 y superiores) la funciónrange ()
funciona comoxrange ()
yxrange ()
ha sido eliminada. Los programadores que usan Python 2.7 pueden desear usarxrange ()
para obtener eficiencia, pero nos quedaremos conrange ()
para que nuestro código funcione con la más amplia variedad de versiones de Python, incluso si sacrifica la eficiencia en algunos casos. Hay una diferencia importante entrerange ()
yxrange ()
en Python 2.7:range ()
devuelve una lista, mientras quexrange ()
devuelve un tipo iterable que carece de algunas características de listas verdaderas. Por ejemplo,nums = range (0, 4)
seguido denums [3] = 1000
daría como resultadonums
referenciando[0, 1, 2, 1000]
, mientras quenums = xrange (0, 4)
seguido denums [3] = 1000
produciría un error.