Fundamentos de Secuencias en Python

PythonPythonBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Las secuencias de Python son colecciones ordenadas de elementos. Están indexadas por enteros.

Tipos de datos de secuencia

Python tiene tres tipos de datos de secuencia.

  • Cadena: 'Hola'. Una cadena es una secuencia de caracteres.
  • Lista: [1, 4, 5].
  • Tupla: ('GOOG', 100, 490.1).

Todas las secuencias están ordenadas, indexadas por enteros y tienen una longitud.

a = 'Hola'               ## Cadena
b = [1, 4, 5]             ## Lista
c = ('GOOG', 100, 490.1)  ## Tupla

## Orden indexado
a[0]                      ## 'H'
b[-1]                     ## 5
c[1]                      ## 100

## Longitud de la secuencia
len(a)                    ## 5
len(b)                    ## 3
len(c)                    ## 3

Las secuencias se pueden replicar: s * n.

>>> a = 'Hola'
>>> a * 3
'HolaHolaHola'
>>> b = [1, 2, 3]
>>> b * 2
[1, 2, 3, 1, 2, 3]
>>>

Las secuencias del mismo tipo se pueden concatenar: s + t.

>>> a = (1, 2, 3)
>>> b = (4, 5)
>>> a + b
(1, 2, 3, 4, 5)
>>>
>>> c = [1, 5]
>>> a + c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: solo se puede concatenar tupla (no "lista") a tupla

Rebanado

Rebanar significa tomar una subsecuencia de una secuencia. La sintaxis es s[start:end]. Donde start y end son los índices de la subsecuencia que desees.

a = [0,1,2,3,4,5,6,7,8]

a[2:5]    ## [2,3,4]
a[-5:]    ## [4,5,6,7,8]
a[:3]     ## [0,1,2]
  • Los índices start y end deben ser enteros.
  • Las rebanadas no incluyen el valor final. Es como un intervalo semiabierto de matemáticas.
  • Si se omiten los índices, por defecto se toman el principio o el final de la lista.

Reasignación de rebanadas

En las listas, las rebanadas se pueden reasignar y eliminar.

## Reasignación
a = [0,1,2,3,4,5,6,7,8]
a[2:4] = [10,11,12]       ## [0,1,10,11,12,4,5,6,7,8]

Nota: La rebanada reasignada no necesita tener la misma longitud.

## Eliminación
a = [0,1,2,3,4,5,6,7,8]
del a[2:4]                ## [0,1,4,5,6,7,8]

Reducciones de secuencia

Hay algunas funciones comunes para reducir una secuencia a un solo valor.

>>> s = [1, 2, 3, 4]
>>> sum(s)
10
>>> min(s)
1
>>> max(s)
4
>>> t = ['Hola', 'Mundo']
>>> max(t)
'Mundo'
>>>

Iteración sobre una secuencia

El bucle for itera sobre los elementos de una secuencia.

>>> s = [1, 4, 9, 16]
>>> for i in s:
...     print(i)
...
1
4
9
16
>>>

En cada iteración del bucle, obtienes un nuevo elemento con el que trabajar. Este nuevo valor se coloca en la variable de iteración. En este ejemplo, la variable de iteración es x:

for x in s:         ## `x` es una variable de iteración
  ...statements

En cada iteración, el valor anterior de la variable de iteración se sobrescribe (si existe). Después de que el bucle finalice, la variable conserva el último valor.

Declaración break

Puedes usar la declaración break para salir de un bucle anticipadamente.

for name in namelist:
    if name == 'Jake':
        break
  ...
  ...
statements

Cuando se ejecuta la declaración break, sale del bucle y pasa a las siguientes statements. La declaración break solo se aplica al bucle más interno. Si este bucle está dentro de otro bucle, no romperá el bucle externo.

Declaración continue

Para omitir un elemento y pasar al siguiente, utiliza la declaración continue.

for line in lines:
    if line == '\n':    ## Omite líneas en blanco
        continue
    ## Más declaraciones
 ...

Esto es útil cuando el elemento actual no es de interés o debe ser ignorado en el procesamiento.

Bucle sobre enteros

Si necesitas contar, utiliza range().

for i in range(100):
    ## i = 0,1,...,99

La sintaxis es range([start,] end [,step])

for i in range(100):
    ## i = 0,1,...,99
for j in range(10,20):
    ## j = 10,11,..., 19
for k in range(10,50,2):
    ## k = 10,12,...,48
    ## Observa cómo cuenta de 2 en 2, no de 1 en 1.
  • El valor final nunca se incluye. Imita el comportamiento de los rebanados.
  • start es opcional. Valor predeterminado 0.
  • step es opcional. Valor predeterminado 1.
  • range() calcula los valores según sea necesario. No almacena realmente una gran cantidad de números.

Función enumerate()

La función enumerate agrega un valor de contador adicional a la iteración.

names = ['Elwood', 'Jake', 'Curtis']
for i, name in enumerate(names):
    ## Bucle con i = 0, name = 'Elwood'
    ## i = 1, name = 'Jake'
    ## i = 2, name = 'Curtis'

La forma general es enumerate(secuencia [, start = 0]). start es opcional. Un buen ejemplo de uso de enumerate() es el seguimiento de números de línea mientras se lee un archivo:

with open(filename) as f:
    for lineno, line in enumerate(f, start=1):
     ...

Al final, enumerate es simplemente un atajo práctico para:

i = 0
for x in s:
    statements
    i += 1

Usar enumerate implica menos tecleo y es un poco más rápido.

For y tuplas

Puedes iterar con múltiples variables de iteración.

points = [
  (1, 4),(10, 40),(23, 14),(5, 6),(7, 8)
]
for x, y in points:
    ## Bucle con x = 1, y = 4
    ##            x = 10, y = 40
    ##            x = 23, y = 14
    ##           ...

Al usar múltiples variables, cada tupla se desempaqueta en un conjunto de variables de iteración. La cantidad de variables debe coincidir con la cantidad de elementos en cada tupla.

Función zip()

La función zip toma múltiples secuencias y crea un iterador que las combina.

columns = ['name','shares', 'price']
values = ['GOOG', 100, 490.1 ]
pairs = zip(columns, values)
## ('name','GOOG'), ('shares',100), ('price',490.1)

Para obtener el resultado, debes iterar. Puedes usar múltiples variables para desempaquetar las tuplas como se mostró anteriormente.

for column, value in pairs:
 ...

Un uso común de zip es crear pares clave/valor para construir diccionarios.

d = dict(zip(columns, values))

Ejercicio 2.13: Contando

Prueba algunos ejemplos básicos de conteo:

>>> for n in range(10):            ## Cuenta 0... 9
        print(n, end=' ')

0 1 2 3 4 5 6 7 8 9
>>> for n in range(10,0,-1):       ## Cuenta 10... 1
        print(n, end=' ')

10 9 8 7 6 5 4 3 2 1
>>> for n in range(0,10,2):        ## Cuenta 0, 2,... 8
        print(n, end=' ')

0 2 4 6 8
>>>

Ejercicio 2.14: Más operaciones con secuencias

Experimenta interactivamente con algunas de las operaciones de reducción de secuencias.

>>> data = [4, 9, 1, 25, 16, 100, 49]
>>> min(data)
1
>>> max(data)
100
>>> sum(data)
204
>>>

Intenta iterar sobre los datos.

>>> for x in data:
        print(x)

4
9
...
>>> for n, x in enumerate(data):
        print(n, x)

0 4
1 9
2 1
...
>>>

A veces, los principiantes usan la instrucción for, len() y range() en algún fragmento de código horrible que parece haber salido de las profundidades de un programa C oxidado.

>>> for n in range(len(data)):
        print(data[n])

4
9
1
...
>>>

¡No lo hagas! No solo hace que les duela la vista a todos, es ineficiente en memoria y es mucho más lento. Simplemente usa un bucle for normal si quieres iterar sobre datos. Usa enumerate() si por alguna razón necesitas el índice.

Ejercicio 2.15: Un ejemplo práctico de enumerate()

Recuerda que el archivo missing.csv contiene datos de una cartera de acciones, pero tiene algunas filas con datos faltantes. Usando enumerate(), modifica tu programa pcost.py de modo que imprima un número de línea junto con el mensaje de advertencia cuando encuentra una entrada incorrecta.

>>> cost = portfolio_cost('/home/labex/project/missing.csv')
Fila 4: No se pudo convertir: ['MSFT', '', '51.23']
Fila 7: No se pudo convertir: ['IBM', '', '70.44']
>>>

Para hacer esto, necesitarás cambiar algunas partes de tu código.

...
for rowno, row in enumerate(rows, start=1):
    try:
     ...
    except ValueError:
        print(f'Fila {rowno}: Fila incorrecta: {row}')
✨ Revisar Solución y Practicar

Ejercicio 2.16: Usando la función zip()

En el archivo portfolio.csv, la primera línea contiene los encabezados de columna. En todo el código anterior, los hemos estado descartando.

>>> f = open('/home/labex/project/portfolio.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name','shares', 'price']
>>>

Sin embargo, ¿y si pudieras usar los encabezados para algo útil? Aquí es donde entra en juego la función zip(). Primero, intenta esto para emparejar los encabezados del archivo con una fila de datos:

>>> row = next(rows)
>>> row
['AA', '100', '32.20']
>>> list(zip(headers, row))
[ ('name', 'AA'), ('shares', '100'), ('price', '32.20') ]
>>>

Observa cómo zip() empareja los encabezados de columna con los valores de columna. Aquí hemos usado list() para convertir el resultado en una lista para que puedas verlo. Normalmente, zip() crea un iterador que debe ser consumido por un bucle for.

Esta emparejamiento es un paso intermedio para construir un diccionario. Ahora, intenta esto:

>>> record = dict(zip(headers, row))
>>> record
{'price': '32.20', 'name': 'AA','shares': '100'}
>>>

Esta transformación es uno de los trucos más útiles de los que debes saber cuando estás procesando muchos archivos de datos. Por ejemplo, supongamos que quisieras hacer que el programa pcost.py funcione con varios archivos de entrada, pero sin importar el número real de columna donde aparecen el nombre, las acciones y el precio.

Modifica la función portfolio_cost() en pcost.py de modo que se vea así:

## pcost.py

def portfolio_cost(filename):
 ...
        for rowno, row in enumerate(rows, start=1):
            record = dict(zip(headers, row))
            try:
                nshares = int(record['shares'])
                price = float(record['price'])
                total_cost += nshares * price
            ## Esto captura errores en las conversiones de int() y float() anteriores
            except ValueError:
                print(f'Row {rowno}: Bad row: {row}')
     ...

Ahora, prueba tu función en un archivo de datos completamente diferente portfoliodate.csv que se ve así:

name,date,time,shares,price
"AA","6/11/2007","9:50am",100,32.20
"IBM","5/13/2007","4:20pm",50,91.10
"CAT","9/23/2006","1:30pm",150,83.44
"MSFT","5/17/2007","10:30am",200,51.23
"GE","2/1/2006","10:45am",95,40.37
"MSFT","10/31/2006","12:05pm",50,65.10
"IBM","7/9/2006","3:15pm",100,70.44
>>> portfolio_cost('/home/labex/project/portfoliodate.csv')
44671.15
>>>

Si lo hiciste correctamente, encontrarás que tu programa todavía funciona aunque el archivo de datos tiene un formato de columna completamente diferente al anterior. ¡Eso es genial!

El cambio realizado aquí es sutil, pero significativo. En lugar de que portfolio_cost() esté codificado para leer un solo formato de archivo fijo, la nueva versión lee cualquier archivo CSV y extrae los valores de interés de él. Siempre que el archivo tenga las columnas requeridas, el código funcionará.

Modifica el programa report.py que escribiste en la Sección 2.3 de modo que use la misma técnica para extraer los encabezados de columna.

Intenta ejecutar el programa report.py en el archivo portfoliodate.csv y verifica que produzca la misma respuesta que antes.

✨ Revisar Solución y Practicar

Ejercicio 2.17: Invertir un diccionario

Un diccionario mapea claves a valores. Por ejemplo, un diccionario de precios de acciones.

>>> prices = {
        'GOOG' : 490.1,
        'AA' : 23.45,
        'IBM' : 91.1,
        'MSFT' : 34.23
    }
>>>

Si usas el método items(), puedes obtener pares (clave,valor):

>>> prices.items()
dict_items([('GOOG', 490.1), ('AA', 23.45), ('IBM', 91.1), ('MSFT', 34.23)])
>>>

Sin embargo, ¿y si quisieras obtener una lista de pares (valor, clave) en lugar de eso? Pista: usa zip().

>>> pricelist = list(zip(prices.values(),prices.keys()))
>>> pricelist
[(490.1, 'GOOG'), (23.45, 'AA'), (91.1, 'IBM'), (34.23, 'MSFT')]
>>>

¿Por qué harías esto? En primer lugar, te permite realizar ciertos tipos de procesamiento de datos en los datos del diccionario.

>>> min(pricelist)
(23.45, 'AA')
>>> max(pricelist)
(490.1, 'GOOG')
>>> sorted(pricelist)
[(23.45, 'AA'), (34.23, 'MSFT'), (91.1, 'IBM'), (490.1, 'GOOG')]
>>>

Esto también ilustra una característica importante de las tuplas. Cuando se usan en comparaciones, las tuplas se comparan elemento a elemento, comenzando con el primer elemento. Similar a la forma en que se comparan las cadenas carácter a carácter.

zip() se usa a menudo en situaciones como esta donde necesitas emparejar datos de diferentes lugares. Por ejemplo, emparejar los nombres de columna con los valores de columna para crear un diccionario de valores con nombre.

Tenga en cuenta que zip() no está limitado a pares. Por ejemplo, puedes usarlo con cualquier número de listas de entrada:

>>> a = [1, 2, 3, 4]
>>> b = ['w', 'x', 'y', 'z']
>>> c = [0.2, 0.4, 0.6, 0.8]
>>> list(zip(a, b, c))
[(1, 'w', 0.2), (2, 'x', 0.4), (3, 'y', 0.6), (4, 'z', 0.8))]
>>>

También, tenga en cuenta que zip() se detiene una vez que la secuencia de entrada más corta se agota.

>>> a = [1, 2, 3, 4, 5, 6]
>>> b = ['x', 'y', 'z']
>>> list(zip(a,b))
[(1, 'x'), (2, 'y'), (3, 'z')]
>>>

Resumen

¡Felicitaciones! Has completado el laboratorio de Secuencias. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.