Tipos de Datos en Python: Listas

El tipo de dato list representa la estructuras de datos más popular y ampliamente empleada en Python; constituyen lo que podríamos llamar el “Caballo de Batalla” entre las estructuras de datos del lenguaje. Las listas son secuencias mutables de objetos arbitrarios y heterogéneos, que pueden expandirse a medida que agregamos nuevos elementos. Los elementos que componen una lista mantienen el mismo orden en que fueron agregados a esta y podremos acceder a ellos a través de sus índices, empleando el operador [i], donde i representa el índice del elemento al que queremos acceder. Las operaciones con índices funcionan tal y como funcionan en las demás secuencias de Python, como las tuplas por ejemplo.

La versatilidad de las listas hace de ellas el candidato número uno en disímiles situaciones, aunque no siempre son el candidato perfecto. Constituyen un tipo de dato muy empleado en Python, y en ocasione se abusa de su uso, lo que puede ir en detrimento de la eficiencia de nuestros programas.

Algunos ejemplos de literales válidos del tipo list en son:

[]

[1]

[1, 2, 3]

[1, 'Two', [4, 5], 6.0]

['Welcome', 'to', 'Python', 'Scouts']

[('Tuple', 'inside'), {'and': 'a dictionary too'}]

El tipo list se define como una clase de la forma:

class list([iterable]):

El constructor de esta clase devuelve una lista cuyos elementos quedan ordenados en la misma forma que lo estaban los elementos de iterable al momento de la creación. Este iterabel puede ser una secuencia, un contenedor que soporte iteración o un iterador. Si iterable ya es una lista, pues obtendremos una copia de esta.

Las listas pueden definirse de varias maneras:

  • Usando un par de corchetes creamos una lista vacía: []

  • Usando corchetes y separando los elementos contenidos en estos mediante comas: [1, 2, 3]

  • Usando comprensión de listas: [x for x in iterable]

  • Usando el constructor: list(), crea una lista vacía; list([iterable]), crea una lista a partir de los elementos de iterable

Si empleamos la clase list para crear listas, podemos proceder de la forma siguiente:

>>> l = list() # Lista vacía

>>> type(l)

<class 'list'>

>>> l

[]

>>> l1 = list([1, 2, 3]) # Creamos la lista a partir de una lista

>>> type(l1)

<class 'list'>

>>> l1

[1, 2, 3]

>>> l2 = list('abc') # Creamos la lista a partir de una cadena

>>> type(l2)

<class 'list'>

>>> l2

['a', 'b', 'c']

Las Listas como Arreglos Dinámicos

Como ya hemos mencionado, las listas son estructuras de datos que tienen la capacidad de variar su tamaño en función de nuestras demandas. De esta forma, podemos añadir o eliminar elementos de una lista, sin límite aparente para el tamaño de la lista en cuestión. Esto es posible debido a que Python implementa las listas empleando un algoritmo denominado Arreglo dinámico (Dynamic Array). La primera clave para proveer la semántica de un Arreglo Dinámico es que el arreglo de base siempre cuente con mayor capacidad que la longitud actual de la lista. Por ejemplo, si hemos creado una lista con cinco elementos, el arreglo de base puede tener capacidad para ocho y por tanto podremos agregar fácilmente tres elementos más. Cuando agregamos elementos a la lista y la capacidad extra se agota, la clase list requiere de un nuevo arreglo de base con mayor capacidad, para poder almacenar más datos. Este arreglos se crea automáticamente, de forma tal que sus elementos iniciales coincidan con los elementos de la lista anterior. Una evidencia empírica de que las listas están implementadas de esta forma en Python, la podemos obtener si ejecutamos el scritp siguiente:

file: dynamic_list.py

import sys

data = [ ]

for k in range(10):

    a = len(data)

    b = sys.getsizeof(data)

    print('Length: {0:3d}; Size in bytes: {1:4d}'.format(a, b))

    data.append(None)

Si lo ejecutamos desde una consola:

$ python3 dynamic_list.py

Length: 0; Size in bytes: 64

Length: 1; Size in bytes: 96

Length: 2; Size in bytes: 96

Length: 3; Size in bytes: 96

Length: 4; Size in bytes: 96

Length: 5; Size in bytes: 128

Length: 6; Size in bytes: 128

Length: 7; Size in bytes: 128

Length: 8; Size in bytes: 128

Length: 9; Size in bytes: 192

Nos damos cuenta de que el tamaño de nuestra lista crece en la medida en que agregamos elementos más allá del espacio reservado. De esta forma, cuando la lista tiene, por ejemplo, de cinco a ocho elementos, ocupa un espacio fijo de 128 bytes y así sucesivamente.

Operaciones con Listas

Las listas implementan todas las operaciones y los operadores comunes a las secuencias y a los tipos de datos mutables, como por ejemplo: contar sus elementos, añadir elementos, remover elementos, identificar el índice de un elemento determinado, copiar, extender, entre otros. Además de estos, las listas implementan el método sort(), que es propio de ellas y que ordena los elementos de la lista en el lugar (dentro de la propia lista) empleando solamente la comparación entre ellos.

El método sort() se define de la forma siguiente:

sort(*, key=None, reverse=False)

Los parámetros key y reverse son parámetros keyword exclusivos. key nos servirá para pasar una función de ordenamiento, como por ejemplo:

>>> l = ['a', 'B', 'd', 'C']

>>> l.sort()

>>> l

['B', 'C', 'a', 'd']

>>> l = ['a', 'B', 'd', 'C']

>>> l.sort(key=str.lower)

>>> l

['a', 'B', 'C', 'd']

Por su parte el parámetro reverse es de tipo booleano y se explica casi por si solo. Su función es precisamente la de ordenar en reversa.

>>> l = ['a', 'B', 'd', 'C']

>>> l.sort(key=str.lower, reverse=True)

>>> l

['d', 'C', 'B', 'a']

El resto de las operaciones con listas funcionan de forma tradicional. Veamos algunos ejemplos:

>>> a_list = ['a']

>>> a_list = a_list + [2.0, 3] # Concatenación

>>> a_list

['a', 2.0, 3]

>>> a_list.append(True) # Añadimos elementos al final

>>> a_list

['a', 2.0, 3, True]

>>> a_list.extend(['four', 'Ω']) # Extendemos la lista

>>> a_list

['a', 2.0, 3, True, 'four', 'Ω']

>>> a_list.insert(0, 'Ω') # Insertamos un elemento en el primer lugar

>> a_list

['Ω', 'a', 2.0, 3, True, 'four', 'Ω']

Como observación, debemos notar que existe una diferencia sutil entre append() y extend(). La primera recibe como argumento un objeto determinado y lo adiciona al final de la lista sin realizar ninguna otra operación, mientras que extend() recibe un único objeto, que es siempre una lista, y lo que hace es añadir los elementos de esta lista al final de la lista original.

>>> a_list = ['a', 'b', 'c']

>>> a_list.extend(['d', 'e', 'f'])

>>> a_list

['a', 'b', 'c', 'd', 'e', 'f']

>>> len(a_list)

6

>>> a_list.append(['g', 'h', 'i'])

>>> a_list

['a', 'b', 'c', 'd', 'e', 'f', ['g', 'h', 'i']]

>>> len(a_list)

7

Buscando Valores en una Lista

En ocasiones deseamos saber si un elemento está incluido dentro de una lista o no. Para esto disponemos de varias opciones, por ejemplo, podemos emplear el método count():

>>> a_list = ['a', 'b', 'c', 'd', 'Python', 'Scouts']

>>> a_list.count('Python')

1

>>> a_list.count(2)

0

También podemos valernos del operador in para determinar si el elemento que buscamos está o no, dentro de la lista:

>>> 'Scouts' in a_list

True

>>> 2 in a_list

False

Finalmente, en ocasiones necesitamos información adicional referida a la posición del elemento que buscamos, en este caso podemos emplear la función index():

>>> a_list = ['a', 'b', 'c', 'd', 'Python', 'Scouts']

>>> a_list.index('Python')

4

En este caso debemos notar que si el elemento no está en la lista, index() devuelve un ValueError.

>> a_list.index(2)

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

ValueError: 2 is not in list

Chequeo de Límites

Aunque las listas no tienen un tamaño fijo y se auto-expanden en la medida en que añadimos elementos, constituye un error tratar de acceder a índices más allá del tamaño actual de la lista:

>>> a_list = ['a', 'b', 'c', 'd', 'Python', 'Scouts']

>>> a_list[10]

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

IndexError: list index out of range

También es un error tratar de asignar valores a elementos con índices no existentes:

>>> a_list[10] = 'Welcome'

Traceback (most recent call last):

    File "<stdin>", line 1, in <module>

IndexError: list assignment index out of range

Esto es totalmente intencional en Python, que en lugar de expandir incorrectamente la lista, nos envía un error para que podamos evitar problemas graves como el típico desbordamiento del buffer en lenguajes como C. De esta forma, para hacer crecer las listas, debemos emplear siempre los métodos disponibles como es el caso de append() y/o extend().

Cuando las Listas no son la Respuesta

Aunque las listas son estructuras de datos muy flexibles y fáciles de usar, en determinadas situaciones no constituyen la mejor opción. Por ejemplo, si queremos almacenar 10 millones de números de coma flotante, un array será mucho más eficiente que una lista. Si por otro lado, necesitamos realizar continuamente operaciones de adición y supresión de elementos en los extremos de la lista, entonces un deque será la estructura de datos recomendada. Los conjuntos (set) por su parte, están optimizados para lograr eficiencia y rapidez en operaciones donde debemos chequear continuamente si un determinado elementos pertenece o está dentro de la lista. La moraleja de esta historia es que debemos conocer muy bien las características de las estructuras de datos disponibles en Python, para poder hacer una elección que satisfaga nuestras necesidades y de esta forma evitar “casarnos” con las listas.

Lecturas Recomendadas

Para profundizar en el estudio y conocimiento del tipo de datos list recomendamos la lectura de la documentación oficial de Python en su acápite 4.6.4. Lists. Recomedamos además la lectura del Capítulo 4 “Introducing Python Object Types”, acápite “Lists del libro “Learnig Python”5ta Edición, por Mark Lutz, publicado por la Editorial O’Reilly.

Ok, esto es todo por ahora, si este artículo te resultó interesante y/o útil, compártelo para que otros también puedan acceder a él. Déjanos tus comentarios y podremos mejorar nuestros contenidos.

Gracias de antemano,

lpozo

2 comentarios

    • myth en 17 octubre, 2018 a las 6:11 pm

    Hay otros tipos de listas bastante útiles, como la linked list o la skip list.

    1. Claro, ese puede ser tema para otra entrada. Gracias por tu comentario.
      saludos,
      lpozo

Los comentarios han sido desactivados.