Mostrando entradas con la etiqueta Tupla. Mostrar todas las entradas
Mostrando entradas con la etiqueta Tupla. Mostrar todas las entradas

viernes, 16 de mayo de 2025

LENGUAJES . Python

Listas y tuplas. Operadores

Colecciones de datos

Los operadores son, como ya sabemos, componentes de todo lenguaje de programación que permiten manipular los datos referenciados en las variables de diferentes maneras, según el tipo de dato y de operador de que se trate. También las colecciones de datos admiten la acción de operadores. Las listas y las tuplas comparten los mismos.

Los operadores disponibles en listas y tuplas son los siguientes:

  • : Permite obtener segmentos del contenido de ambas colecciones.
  • + y * Permiten realizar las operaciones sobre listas y tuplas. Veremos cuáles.
  • in / not in Permiten verificar la presencia / no presencia de determinado dato dentro del conjunto.
  • Los operadores relacionales <, <=, >, >= y !=, cuyas funciones describiremos en su momento.

Aunque figure en primer lugar, no trataré el operador : en esta entrada, dada la relación que mantiene con el acceso a los datos. Por este motivo desplazo su análisis a la entrada correspondiente.

Inicio este análisis con los operadores + y *, los cuales cumplen funciones específicas con listas y tuplas, diferentes de las que permiten ejecutar en variables.

  • El operador + permite la unión o concatenación de los elementos de varias listas. Como cabe suponer por lo que sabemos de los operaciones aritméticos, nos permiten realizar operaciones de suma y multiplicación con listas y tuplas.

lista1 = list([1,2,3,4])
lista2 = list([5,6,7,8])
print(lista1 + lista2) 

-> devuelve [1,2,3,4,5,6,7,8]

  • Este mismo operador, aplicado a tuplas funciona exactamente igual, aunque lógicamente devuelve una tupla (1,2,3,4,5,6,7,8)
  • La operación * tiene como efecto la repetición de la lista o la tupla sujeta a tal operación, siendo indiferente que el operador se sitúe antes o después de la lista o tupla. Así que print(3 * lista1) devuelve [1,2,3,4,1,2,3,4,1,2,3,4] y print(tupla1 * 2) devuelve (1,2,3,4,1,2,3,4). En ambos casos el número debe ser entero (ni 3.1 ni 2.4 se admiten como "multiplicadores")
  • Ambos operadores tienen el mismo efecto con independencia de la naturaleza de los elementos de la lista o de la tupla.
  • En ambos casos no está permitido el trabajo con listas y tuplas simultáneamente. No es posible ni lista + tupla, ni tupla * lista, ni ninguna otra combinación o permutación de elementos.
  • Los operadores in / not in permiten identificar si un dato forma parte o no de una lista o de una tupla. Ambos devuelven True o False según el resultado de la operación. Ejemplos para lista1 y tupla1 anteriores:


print(5 in lista1) #devuelve False
print(1 in lista1) #devuelve False

print(5 not in lista1) #devuelve True
print(1 not in lista1) #devuelve True

print(5 in tupla1) #devuelve False
print(1 in tupla1) #devuelve False

print(5 not in tupla1) #devuelve True
print(1 not in tupla1) #devuelve True


Los operadores relacionales permiten comparar dos listas entre sí, elemento a elemento y según criterios específicos en función del tipo de elemento. También devuelven True o False, según el resultado de la comparación. Veamos algunas comparaciones, tomando como referencia las listas y las tuplas creadas antes. Dada la multiplicidad de opciones me limitaré a unos pocos ejemplos, aunque los resultados se pueden hacer extensibles al resto de los operadores:



#Operadores relacionales con listas

print(lista1 == lista1) #Devuelve True

print(lista1 != lista2) #Devuelve True

#Operadores relacionales con tuplas

print(tupla1 == tupla1) #Devuelve True

print(tupla1 != tupla2) #Devuelve True

#Operadores relacionales con listas y tuplas

print('Operadores relacionales con listas y tuplas')

print(lista1 == tupla1) #Devuelve False por ser una lista y la otra tupla, aunque el contenido es el mismo


jueves, 15 de mayo de 2025

LENGUAJES. Python

Listas y tuplas. Automatizar contenido

Colecciones de datos

Aunque la forma más frecuente de crear colecciones de datos es introducirlos directamente mediante teclado o acceder a documentos en los que estos se encuentren disponibles, en ocasiones es necesario crear colecciones de datos numéricos más o menos amplias cuyos elementos mantienen entre sí una relación, lo que hace que sea posible automatizar su creación. Para estos casos es muy útil conocer procedimientos de creación automática de colecciones de datos. Y existen, tanto para listas y como para tuplas.

El primer procedimiento para crear listas (y tuplas) automáticamente es el que utiliza la función range(). Esta función precisa tres parámetros: el valor de inicio, el valor final y el incremento. Un ejemplo: range(2,12,2 nos permite crear una colección de datos que inicia en el número 2 y finaliza en el 10 (el 12 no está incluido), incrementándose el valor inicial de 2 en 2 (2 - 4 - 6...).

Utilizamos el constructor de listas y creamos automáticamente una que cumple esos criterios. Si la lista es pequeña podemos hacerlo manualmente, pero si contiene muchos elementos es más funcional usar la función range() que acabo de explicar:

  • lista_1 = list(range(2,100,2)) crea una lista con todos los números pares comprendidos entre 2 y 100 (100 no incluido).
  • tupla_1 = tuple(range(2,100,2)) tiene el mismo efecto práctico, aunque en este caso se trata de una tupla, con todo lo que esto implica respecto a inmutabilidad.

Existen otros procedimientos que además de permitirnos crear colecciones de datos numéricos más complejas que también es necesario conocer si nos encontramos con script que utilicen estas expresiones. Me estoy refiriendo a la creación de listas o tuplas mediante operadores, bucles y condicionales. Veamos algunos ejemplos:

  • lista_3 = [x * 2 for x in lista_1] es una lista de valores numéricos que utiliza lista_1 (recuerda: números pares comprendidos entre 2 y 98) multiplicando por 2 cada uno de ellos. Mediante for x in lista_1 recorre esa lista.
  • La tupla_2 =tuple([x * 2 for x in tupla_1]) hace lo mismos pero creando una tupla y usando una tupla como base. Observa que ahora necesitamos usar el constructor y una combinación de ([])
  • La lista_4 = [x**2 for x in lista_1 if x > 8] hace los mismo que lista_2, pero ahora añadimos como condición para la creación de la colección de datos que el valor original de lista_1 sea superior a 8 if x > 8)
  • Finalmente, en lista_6 = [x * 3 for x in range(2,32,2) if x >10] utilizamos directamente la función range() para crear automáticamente la colección de datos de base for x in range(2,32,2) y mantenemos una condición: que los valores resultantes sean superiores a 10 if x >10

Digo finalmente para no prolongar esta casuística más de lo necesario, porque aun caben otras combinaciones, incluyendo la de listas y tuplas y el uso de doble bucle en función de una doble variable lista_5 = [x + y for x in lista_1 for y in lista_2]

Documento. En este enlace te dejo acceso a un script con ejemplos, incluidos los explicados en esta entrada.

martes, 13 de mayo de 2025

LENGUAJES. Python

Acceso a listas y a tuplas

Colecciones de datos

Aunque listas y tuplas son colecciones de datos diferentes, ya dijimos que comparten muchas características y comportamientos; entre estos últimos están su posible contenido y el modo en que podemos acceder a él.

El acceso a los elementos de las listas y las tuplas es un procedimiento de uso muy frecuente y de mucha utilidad práctica, por lo que interesa conocer cómo realizarlo. Ya sabemos cómo es el procedimiento básico por haberlo visto al hablar de ambas colecciones de datos (v.g. print(mi_tupla_a[0] nos devuelve en pantalla el primer elemento de la tupla), pero esto nos sirve cuando la tupla o la lista contienen elementos simples (vg mi_lista_a = [12,23,43,56]), pero, ¿ cómo accedemos a los elementos complejos, a las listas o tuplas anidadas dentro de la lista o tupla principal?.

Un ejemplo de ello podría ser el siguiente: una lista que contiene "registros" que se contienen a su vez una lista (1):

  • mi_lista_b = [['Lucía','Martínez Alonso','P4'],['Mario','Carcedo Suárez','P3'],['Carlota','Álvarez López','P2']]

Veamos mediante código distintas posibilidades de acceso al contenido de esta lista. Denominaré registro a cada una de las tres sublistas y dato a cada uno de los elementos de cada uno de los registros (dato -> nombre, dato -> apellidos, dato -> curso)



#Acceso a la lista completa

print(mi_lista_b)

#Captura del primer registro

reg1 = mi_lista_b[0]

#Acceso al primer registro de la lista

print(mi_lista_b[0])

#Acceso al primer registro que pasamos previamente a reg1

print(reg1)

#Acceso al primer dato (dato -> nombre) de cada uno de los registros de la lista

reg_nombres = [[mi_lista_b[0][0]], [mi_lista_b[1][0]],[mi_lista_b[2][0]]]

print(reg_nombres)


#Acceso al primer dato del nueva lista reg_nombres

print(reg_nombres[0])

#Otro modo de acceso (ahora reg_nombres_b es una lista de elementos imples

reg_nombres_b = [mi_lista_b[0][0], mi_lista_b[1][0],mi_lista_b[2][0]]

print(reg_nombres_b)


En este script estamos jugando con diferentes formas de acceder al contenidos de una lista compleja (lista de listas). Podemos diferenciar el modo de acceder al conjunto de la lista print(mi_lista_b) y a su primer elemento print(mi_lista_b[0]). En el primer caso nos devuelve todos los registros de la lista (la lista con sus sublistas...

  • [['Lucía', 'Martínez Alonso', 'P4'], ['Mario', 'Carcedo Suárez', 'P3'], ['Carlota', 'Álvarez López', 'P2']]

... y en el segundo sólo el primer registro (la primera sublista)

  • ['Lucía', 'Martínez Alonso', 'P4']

Si lo que queremos es acceder al primer dato de la primera sublista, deberemos expresarlo como sigue:

  • [mi_lista_b[0][0]]) - nos devuelve Lucia

Observa que empleamos una pareja de corchetes para identificar el primer registro de la lista principal (que aquí ni se nombra) reg_nombres_b y que se concreta como mi_lista_b[] y a continuación una segunda pareja de corchetes para identificar el dato que deseamos (también el primero [0][0])

Haciendo extensible este procedimiento, podemos crear una lista de listas que contienen como único registro el nombre de cada alumno...

  • reg_nombres = [[mi_lista_b[0][0]], [mi_lista_b[1][0]],[mi_lista_b[2][0]]]

... o, como alternativa, una lista de elementos simples con el mismo contenido

  • reg_nombres_b = [mi_lista_b[0][0], mi_lista_b[1][0],mi_lista_b[2][0]]

La diferencia es sutil pero fundamental: en el primer caso delimitamos doblemente con corchetes cada elemento, en el segundo no. Con el primer procedimiento obtenemos una lista de listas simples (de un sólo elemento o dato cada una)...

  • [['Lucía'], ['Mario'], ['Carlota']]

... y con el segundo una lista simple de tres elementos (2)

  • ['Lucía', 'Mario', 'Carlota']

Además de acceder a elementos concretos de la lista o de la tupla, también podemos hacerlo a un subconjunto de datos mediante el operador :. De este operador ya hablamos en esta entrada, pero dejamos aplazado su tratamiento para relacionarlo con los procedimientos de acceso al contenido, motivo por el que corresponde ahora tratarlo.

Este operador presenta una formulación básica de uso lista/tupla[inicio:fin] en la que se indica la posibilidad de delimitar el inicio del subconjunto y su final (mejor dicho, el elemento previo a su final). Por ejemplo, para lista0 = [1,2,3,4,5,6,7,8,9], print(lista0[0:3]) nos devuelve [1,2,3], dado que se cuentan los elementos empezando por 0 y el elemento posición 3 es 4, por lo que el anterior (elemento 2 -> 3).

Ahora podemos omitir tanto el elemento inicio como el elemento fin. En el primer caso (vg. lista0[:3]) el resultado será el mismo que antes ([1,2,3]) ya que la ausencia del delimitador inicio implica partir del primer elemento de la lista (o de la tupla). Si omitimos el elemento final (vg lista0[2:]) implícitamente se supone que deseamos ir desde el elemento inicio (posición 2 -> 3) hasta el final de la lista. En consecuencia, para lista0 -> [3,4,5,6,7,8,9].

Otra forma de acceder a segmentos del contenido de listas o tuplas es empleando índices negativos...



print(lista0[-5:-3]) 

#-> devuelve [5,6]

print(tupla0[-2:]) 

#-> Para tupla0, igual resultado que para lista0: devuelve (8,9)


... debido a que los valores negativos remiten al orden inverso en el acceso al contenido de listas y tuplas.

NOTAS
1 El mismo ejemplo podríamos plantear mediante una tupla de tuplas, una lista de tuplas o una tupla de listas.
2 Podríamos hacer exactamente lo mismo con una tupla y obtendríamos los mismos resultados

jueves, 13 de junio de 2024

LENGUAJES. Python.

Tuplas. Manejo básico

Colecciones de datos

Después de las listas, las tuplas son el segundo tipo de colecciones de datos del lenguaje Python. En esta entrada, siguiendo el modelo de la anterior, recordaremos sus características y aprenderemos a manejarlas.

La palabra tupla no existe en español, aunque sí en el lenguaje matemático, derivada de la generalización de la secuencia dupla, tripla.. n-tupla..., esto es, como lista ordenada finita de elementos.

En informática, una tupla es una lista inmutable y ese es el significado que tiene también en Python. En consecuencia, una tupla no es más que una lista cerrada, que no se puede modificar una vez creada, lo que equivale a decir que es un tipo específico de lista.

Mientras que una lista se pueden modificar (por lo que podríamos redefinirla como listas abierta o mutable), una tupla (o lista cerrada o inmutable) no se pueden modificar.

En Python para diferenciar lista de tupla se modifica su sintaxis básica: mientras la lista se identifica por el uso de corchetes, en la tupla se emplea el paréntesis...

  • miLista = ["perro","gato","lobo"]
  • miTupla = ("perro","gato","lobo")

... pero en ambas los datos se separan por comas y es el valor de posición (el índice) que ocupan en su seno lo que permite identificarlos (1):

  • print(miLista[1]) - gato
  • print(miTupla[1]) - gato

Al ser las tuplas inmutables las podemos considerar equivalentes a las constantes frente a las variables (que serían las listas), por lo que su utilidad es equivalente a la de aquellas. Los tuplas no admiten ciertas operaciones que sí son posibles en las listas, como añadir elementos, modificarlos o borrarlos, por lo que no cambian ni de estructura ni de contenidos a lo largo de todo el script.

No obstante, una tupla no deja de ser una colección de datos que puede contener distintos tipos de elementos. Del mismo modo que las listas pueden contener, además de datos de diferentes tipos, otras listas y también tuplas, las tuplas pueden tener la misma composición, incluyendo listas. Y al ser las listas elementos mutables, una tupla que contenga una lista podrá ser modificable, no en cuanto tupla, pero sí ese elemento de la tupla definido como lista.

Detengámonos un momento a analizar estas cuestiones.



miTupla = ("casa","mesa")
print(miTupla) -> ('casa', 'mesa')
miTupla[0]="silla"

#-> Devuelve error 

Traceback (most recent call last):
  File "", line 1, in 
    miTupla[0]="silla"
TypeError: 'tuple' object does not support item assignment


Aquí hemos definido una tupla de dos elementos. Si queremos modificar uno de ellos mediante asignación (como hacemos en una lista) el resultado es un mensaje de error que nos informa que el objeto tupla no soporta la asignación de ítem, o sea, que no se puede modificar. Tampoco podríamos hacer otros cambios que sí son posibles en las listas, como añadir un nuevo elemento o borrarlo.



miLista=[1,2,3]
miTupla=("casa","mesa",miLista)

print(miTupla) -> ('casa', 'mesa', [1, 2, 3])

miTupla[0]="armario" 

# -> Devuelve error
Traceback (most recent call last):File "", line 1, in  miTupla[0]="armario". TypeError: 'tuple' object does not support item assignment 

print(miTupla[2][0]) 

#-> 1
miTupla[2][0]=0
print(miTupla) -> ('casa', 'mesa', [0, 2, 3])


En este segundo ejemplo, la tupla contiene tres elementos, siendo el tercero [2], una lista definida con anterioridad miLista. Podemos comprobar de nuevo que si queremos modificar el elemento [0] nos devuelve error, pero si lo que deseamos es modificar el elemento [2][0] (tercer elemento de la tupla y primero de esa lista), es perfectamente posible hacerlo: la orden miTupla[2][0]=0 no devuelve error y se ejecuta correctamente.

  • print(miTupla) ->('casa', 'mesa', [0, 2, 3])

Para finalizar esta entrada, advirtiendo antes que aun quedan muchas cosas por aprender de las listas y de las tuplas, vamos a ver una aplicación práctica combinando listas y tuplas y bucles o ciclos.

Este ejercicio se puede considerar complementario tanto de esta entrada como de la anterior, ya que desarrolla un tema que en aquella quedó indicado pero no resuelto. Dije entonces que podíamos crear listas sin elementos (sin contenido), pero no dije qué utilidad podrían tener tales listas. Ahora veremos una de ellas. Utilizar una tupla como marco de referencia, concreta también una de sus utilidades (2): respetar la estructura básica de una formulación asimilable a base de datos.

El código resultante queda aquí a tu disposición para que lo estudies. La idea es que crees tú algún otro parecido y compares tu solución con la mía, que no es perfecta, puesto que el resultado final (Ejemplo para dos alumnos hipotéticos)...

  • ['Carmela', 'Alejandro'], ['Álvarez López', 'Ramírez Suárez'], ['1', '1', '1', '0', '1', '0', '1', '1', '0', '0', '1', '1', '1', '0']

... no es del todo satisfactorio: mi idea era obtener el siguientes resultado para el tercer componente de la tupla (dos elementos lista en lugar de un único elemento)

  • [['1', '1', '1', '0', '1', '0', '1'],[ '1', '0', '0', '1', '1', '1', '0']]

... pero no me ha sido posible... por el momento.

Aunque este error relativo respecto al objetivo es importante y habrá que estudiar cómo remediarlo, sí hay algunos logros de interés que te quiero comentar brevemente:

  • En la entrada sobre listas decía que la expresión lstPalabras = lstPalabras + ["mesita"] permitía añadir un nuevo elemento a la lista, y es correcto, pero para añadir un elemento en otros contextos de mayor complejidad necesitamos emplear una función append() como puedes observar en el ejemplo:
    • vNombre=input("Nombre: ")
    • lstNombres.append(vNombre)
  • Podemos declarar listas vacías y cumplimentarlas mediante un bucle en función de una decisión previa: en mi ejemplo, el número de alumnos del listado, que sirve para establecer el número de ciclos o iteraciones que realiza el bucle for
    • vNumAl = int(input("Dime el número de alumnos y alumnas del grupo: "))
    • (...)
    • for i in range(vNumAl):
  • Podemos anidar un bucle for dentro de otro bucle, generando estructuras complejas. En mi ejemplo el bucle principal contiene un bucle secundario. Las iteraciones del primero vienen dadas interactivamente, pero las del segundo vienen determinadas por un valor implícito: el número de ítem de la prueba for u in range(7):
  • Aunque lo que manejamos son listas (y por eso las podemos modificar), la estructura principal es una tupla, ya que queremos que su estructura básica (la del "registro") sea invariable (3).
NOTAS
1 Como curiosidad, obsérvese que en ambos casos usamos corchetes para contener el índice.
2 Precisamente aquella en la que su rigidez se puede considerar una virtud.
3 Precisamente la limitación de este algoritmo es no haber conseguido reproducir la forma esperada en un diseño de base de datos: ser resultante de la unión o consulta de dos tablas: datos personales y resultados. Si obtengo un resultado mejor, lo publicaré.