5. Estructuras de datos¶
Este capítulo describe en más detalle algunas cosas que ya has aprendido y agrega algunas cosas nuevas también.
5.1. Más sobre listas¶
The list data type has some more methods. Here are all of the methods of list objects:
- list.append(value, /)
Add an item to the end of the list. Similar to
a[len(a):] = [x].
- list.extend(iterable, /)
Extend the list by appending all the items from the iterable. Similar to
a[len(a):] = iterable.
- list.insert(index, value, /)
Inserta un ítem en una posición dada. El primer argumento es el índice del ítem delante del cual se insertará, por lo tanto
a.insert(0, x)inserta al principio de la lista ya.insert(len(a), x)equivale aa.append(x).
- list.remove(value, /)
Remove the first item from the list whose value is equal to value. It raises a
ValueErrorif there is no such item.
- list.pop(index=-1, /)
Remove the item at the given position in the list, and return it. If no index is specified,
a.pop()removes and returns the last item in the list. It raises anIndexErrorif the list is empty or the index is outside the list range.
- list.clear()
Remove all items from the list. Similar to
del a[:].
- list.index(value[, start[, stop]])
Return zero-based index of the first occurrence of value in the list. Raises a
ValueErrorif there is no such item.Los argumentos opcionales start y end son interpretados como la notación de rebanadas y se usan para limitar la búsqueda a un segmento particular de la lista. El índice retornado se calcula de manera relativa al inicio de la secuencia completa en lugar de hacerlo con respecto al argumento start.
- list.count(value, /)
Return the number of times value appears in the list.
- list.sort(*, key=None, reverse=False)
Ordena los elementos de la lista in situ (los argumentos pueden ser usados para personalizar el orden de la lista, ver
sorted()para su explicación).
- list.reverse()
Invierte los elementos de la lista in situ.
- list.copy()
Return a shallow copy of the list. Similar to
a[:].
Un ejemplo que usa la mayoría de los métodos de la lista:
>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4) # Find next banana starting at position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'
Quizás hayas notado que métodos como insert, remove o sort que únicamente modifican la lista no tienen un valor de retorno impreso – retornan el valor por defecto None. [1] Esto es un principio de diseño para todas las estructuras de datos mutables en Python.
Another thing you might notice is that not all data can be sorted or
compared. For instance, [None, 'hello', 10] doesn’t sort because
integers can’t be compared to strings and None can’t be compared to
other types. Also, there are some types that don’t have a defined
ordering relation. For example, 3+4j < 5+7j isn’t a valid
comparison.
5.1.1. Usar listas como pilas¶
Los métodos de lista hacen que resulte muy fácil usar una lista como una pila, donde el último elemento añadido es el primer elemento retirado («último en entrar, primero en salir»). Para agregar un elemento a la cima de la pila, utiliza append(). Para retirar un elemento de la cima de la pila, utiliza pop() sin un índice explícito. Por ejemplo:
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
5.1.2. Usar listas como colas¶
It is also possible to use a list as a queue, where the first element added is the first element retrieved («first-in, first-out»); however, lists are not efficient for this purpose. While appends and pops from the end of list are fast, doing inserts or pops from the beginning of a list is slow (because all of the other elements have to be shifted by one). See Time complexity of operations on built-in types for more information.
Para implementar una cola, utiliza collections.deque el cual fue diseñado para añadir y quitar de ambas puntas de forma rápida. Por ejemplo:
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.popleft() # The first to arrive now leaves
'Eric'
>>> queue.popleft() # The second to arrive now leaves
'John'
>>> queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
5.1.3. Comprensión de listas¶
Las comprensiones de listas ofrecen una manera concisa de crear listas. Sus usos comunes son para hacer nuevas listas donde cada elemento es el resultado de algunas operaciones aplicadas a cada miembro de otra secuencia o iterable, o para crear un segmento de la secuencia de esos elementos para satisfacer una condición determinada.
Por ejemplo, asumamos que queremos crear una lista de cuadrados, como:
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Nótese que esto crea (o sobreescribe) una variable llamada x que sigue existiendo luego de que el bucle haya terminado. Podemos calcular la lista de cuadrados sin ningún efecto secundario haciendo:
squares = list(map(lambda x: x**2, range(10)))
o, un equivalente:
squares = [x**2 for x in range(10)]
que es más conciso y legible.
Una lista de comprensión consiste de corchetes rodeando una expresión seguida de la declaración for y luego cero o más declaraciones for o if. El resultado será una nueva lista que sale de evaluar la expresión en el contexto de los for o if que le siguen. Por ejemplo, esta lista de comprensión combina los elementos de dos listas si no son iguales:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
y es equivalente a:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
Nótese como el orden de los for y if es el mismo en ambos pedacitos de código.
Si la expresión es una tupla (como el (x, y) en el ejemplo anterior), debe estar entre paréntesis.
>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
File "<stdin>", line 1
[x, x**2 for x in range(6)]
^^^^^^^
SyntaxError: did you forget parentheses around the comprehension target?
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Las comprensiones de listas pueden contener expresiones complejas y funciones anidadas:
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
5.1.4. Listas por comprensión anidadas¶
La expresión inicial de una comprensión de listas puede ser cualquier expresión arbitraria, incluyendo otra comprensión de listas.
Considerá el siguiente ejemplo de una matriz de 3x4 implementada como una lista de tres listas de largo 4:
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
La siguiente comprensión de lista transpondrá las filas y columnas:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Como vimos en la sección anterior, la lista de comprensión anidada se evalúa en el contexto del for que lo sigue, por lo que este ejemplo equivale a:
>>> transposed = []
>>> for i in range(4):
... transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
el cual, a la vez, es lo mismo que:
>>> transposed = []
>>> for i in range(4):
... # the following 3 lines implement the nested listcomp
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
En el mundo real, deberías preferir funciones predefinidas a declaraciones con flujo complejo. La función zip() haría un buen trabajo para este caso de uso:
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
Ver Desempaquetando una lista de argumentos para detalles sobre el asterisco de esta línea.
5.1.5. Unpacking in Lists and List Comprehensions¶
The section on Desempaquetando una lista de argumentos describes the use of * to
«unpack» the elements of an iterable object, providing each one separately as
an argument to a function. Unpacking can also be used in other contexts, for
example, when creating lists. When specifying elements of a list, prefixing an
expression by a * will unpack the result of that expression, adding each of
its elements to the list we’re creating:
>>> x = [1, 2, 3]
>>> [0, *x, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
This only works if the expression following the * evaluates to an iterable
object; trying to unpack a non-iterable object will raise an exception:
>>> x = 1
>>> [0, *x, 2, 3, 4]
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
[0, *x, 2, 3, 4]
TypeError: Value after * must be an iterable, not int
Unpacking can also be used in list comprehensions, as a way to build a new list representing the concatenation of an arbitrary number of iterables:
>>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
>>> [*element for element in x]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Note that the effect is that each element from x is unpacked. This works
for arbitrary iterable objects, not just lists:
>>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
>>> [*element for element in x]
[1, 2, 3, 'c', 'a', 't', 'spam']
But if the objects in x are not iterable, this expression would again raise
an exception.
5.2. La instrucción del¶
Hay una manera de quitar un ítem de una lista dado su índice en lugar de su valor: la instrucción del. Esta es diferente del método pop(), el cual retorna un valor. La instrucción del también puede usarse para quitar secciones de una lista o vaciar la lista completa (lo que hacíamos antes asignando una lista vacía a la rebanada). Por ejemplo:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
del puede usarse también para eliminar variables:
>>> del a
Hacer referencia al nombre a de aquí en más es un error (al menos hasta que se le asigne otro valor). Veremos otros usos para del más adelante.
5.3. Tuplas y secuencias¶
Vimos que las listas y cadenas tienen propiedades en común, como el indexado y las operaciones de rebanado. Estas son dos ejemplos de datos de tipo secuencia (ver