Tiza Paso a paso
Practicar →

Pensamiento Computacional · Unidad 4 — Estructuras de datos

Tuplas

Una tupla es una lista que no se puede modificar. Sirve justamente para eso: dejar claro que ese conjunto de datos no cambia.

Practicar este tema →8 ejercicios que te corrigen paso a paso · gratis, sin cuenta

La explicación

Van con paréntesis

punto = (3, 5)
punto[0]   → 3
len(punto)  → 2

Se indexa y se recorre igual que una lista. La única diferencia es que punto[0] = 9 da error: las tuplas son inmutables.

Para qué sirve algo que no se puede cambiar

Justamente para eso. Una tupla dice "esto es un dato compuesto que no se toca": las coordenadas de un punto, una fecha, el ancho y alto de una imagen. Si más adelante alguien intenta modificarla, Python avisa en vez de dejarlo pasar.

Como regla: lista para una colección de cosas del mismo tipo que puede crecer (las notas de un curso), tupla para un dato con partes fijas (un par de coordenadas).

Empaquetar y desempaquetar

punto = (3, 5)       # empaquetar
x, y = punto         # desempaquetar: x vale 3, y vale 5

Esto ya lo venís usando sin saberlo. Cuando una función hace return a // b, a % b está devolviendo una tupla, y c, r = dividir(7, 2) la está desempaquetando.

De ahí también sale el intercambio en una línea del tema 2.1:

a, b = b, a

La derecha arma la tupla (b, a) y la izquierda la desempaqueta. Por eso no hace falta variable auxiliar: la tupla la hace de intermediaria.

Una tupla de un solo elemento

t = (5)     → esto es el número 5, no una tupla
t = (5,)    → esto sí es una tupla de un elemento

La coma es lo que hace la tupla, no el paréntesis. Es una rareza de Python que conviene conocer porque el error que produce es desconcertante.

Convertir

list((1, 2, 3))   → [1, 2, 3]
tuple([1, 2, 3])  → (1, 2, 3)

Para qué se usan de verdad: devolver más de un valor

Éste es el uso que aparece en cada programa un poco largo, y casi nunca se lo presenta como «tupla»:

def extremos(xs):
    return min(xs), max(xs)

r = extremos([4, 8, 15, 16, 23, 42])  → (4, 42)
mn, mx = extremos([4, 8, 15, 16, 23, 42])  → mn = 4, mx = 42

Una función devuelve un valor, siempre. Lo que pasa con return a, b es que ese único valor es una tupla, y el desempaquetado del otro lado la reparte. Por eso conviene tener el tema claro aunque nunca escribas un paréntesis a propósito: lo estás usando igual.

Lo que una lista no puede hacer

Al no poder cambiar, una tupla sirve de clave de diccionario y una lista no. Los diccionarios recién se ven en el tema que sigue, el 4.5: mirá esto de reojo y quedate con la idea de que una clave tiene que ser algo que no pueda cambiar a mitad de camino.

d = {(0, 0): "origen", (1, 2): "punto"}   → funciona
d = {[0, 0]: "origen"}                    → TypeError: unhashable type: 'list'
Inmutable no significa congelado del todo. Lo que no se puede cambiar es qué objetos están adentro de la tupla, no el estado interno de esos objetos. Si una tupla contiene una lista, la lista sigue siendo modificable:
t = (1, [2, 3])
t[1].append(4)   → t ahora es (1, [2, 3, 4])
t[0] = 9           → TypeError: 'tuple' object does not support item assignment
La segunda línea funciona y la tercera no. Es exactamente la distinción entre modificar y reasignar del tema 4.2, y es de lo que más se pregunta en la evaluación de código.
Un ejercicio resuelto, paso a paso

Una función tiene que recibir una lista de puntos (x, y) y devolver cuántos están en el primer cuadrante (los dos valores positivos).

Cada punto es una tupla, así que se puede desempaquetar directamente en el for:

def en_primer_cuadrante(puntos):
    cuenta = 0
    for x, y in puntos:
        if x > 0 and y > 0:
            cuenta = cuenta + 1
    return cuenta

Con [(1, 2), (-3, 4), (5, 6)]: el primero cuenta, el segundo no (x es negativo), el tercero sí. Devuelve 2.

Fijate el for x, y in puntos. En cada vuelta agarra una tupla y la desempaqueta sola en dos variables. La alternativa sería for p in puntos: y después usar p[0] y p[1], que funciona igual pero se lee mucho peor: x e y dicen qué son, p[0] no.

Por qué tupla y no lista para cada punto. Un punto tiene exactamente dos coordenadas y no va a crecer. La tupla deja eso escrito, y de paso evita que alguien le haga append por error.

Control: probá con un punto en el eje, como (0, 5). Con > no cuenta, que es lo correcto: el origen y los ejes no son parte de ningún cuadrante.

La variante que suelen pedir a continuación: que además de contar, la función devuelva cuántos quedaron sobre los ejes. Dos resultados, un solo return:
return en_cuadrante, en_eje
y del lado de afuera dentro, ejes = contar(puntos). No hace falta ninguna estructura nueva: return a, b arma una tupla y el desempaquetado la reparte en dos variables con nombre.

El control que cierra la variante: los dos números tienen que sumar el total de puntos de la lista. Si suman de menos, hay un punto que no entró en ninguna de las dos ramas — casi siempre por haber usado > en un lado y >= en el otro.

Y uno del nivel del parcial

Una función recibe una lista de tuplas (nombre, nota) y tiene que devolver el nombre del mejor alumno y la lista de los desaprobados (nota menor a 4), ordenada alfabéticamente. Tiene que funcionar con la lista vacía.

La ventaja de recorrer tuplas: se pueden desarmar en el propio for, y el código queda legible sin índices.

for nombre, nota in alumnos:
    ...

Eso es mucho más claro que alumno[0] y alumno[1], y evita el error de cruzar los índices, que con tuplas de tres o cuatro elementos es casi seguro.

La función completa:

def mejor_y_desaprobados(alumnos):
    mejor = None
    desaprobados = []
    for nombre, nota in alumnos:
        if mejor is None or nota > mejor[1]:
            mejor = (nombre, nota)
        if nota < 4:
            desaprobados.append(nombre)
    return (mejor[0] if mejor else None, sorted(desaprobados))

Un solo recorrido para las dos cosas. Se podría hacer en dos ciclos, uno para el mejor y otro para los desaprobados, y estaría bien; en uno solo se lee mejor y se recorre la mitad.

El mejor guarda la tupla entera, no solo el nombre, porque hace falta la nota para comparar en la vuelta siguiente. Recién al final se devuelve el nombre.

El > estricto decide los empates. Con nota > mejor[1], si dos alumnos tienen la misma nota máxima gana el que aparece primero en la lista. Con >= ganaría el último. Las dos son defendibles; lo que hay que hacer es elegir a propósito y decirlo.

El sorted(desaprobados) del final ordena alfabéticamente y devuelve una lista nueva. Con .sort() también se ordena, pero devuelve None, y usarlo dentro de un return es uno de los errores más frecuentes:

return sorted(lista)  → devuelve la lista ordenada ✓
return lista.sort()   → devuelve None ✗

Las pruebas:

datos = [("Ana", 8), ("Beto", 3), ("Caro", 9), ("Dani", 2)]
→ ("Caro", ["Beto", "Dani"])
lista vacía                      → (None, [])
[("Ana", 9), ("Beto", 9)]   → ("Ana", [])   empate: gana el primero

El caso de la lista vacía es el que rompe si no se protege: mejor queda en None y mejor[0] tira un error. El if mejor else None del return lo resuelve en una línea.

Un control rápido: Beto y Dani están ordenados alfabéticamente aunque en la lista original Beto venía antes que Dani por casualidad. Conviene probar con datos donde el orden original y el alfabético no coincidan —por ejemplo poniendo a Dani primero— para que el sorted se note.

Dónde se pierde el punto: comparar contra mejor en vez de contra mejor[1]. Comparar una nota con una tupla tira un error en Python 3; en otros lenguajes compararía cualquier cosa y devolvería un resultado sin sentido.

Dónde se cae la mayoría

Hasta acá la explicación. La práctica es la otra mitad: 8 ejercicios de este tema, en cuatro niveles, que no te dicen sólo si está bien al final sino que te corrigen en cada paso y te explican por qué. Y uno para escribir el programa vos, que se prueba ejecutándolo en Python ahí mismo, en el navegador. Además trae una animación que muestra la idea en movimiento.

Practicar Tuplas →

Dónde entra este tema en el parcial

Los otros temas de la unidad 4

Estructuras de datos

Ver todos los temas de Pensamiento Computacional