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

Python. Lenguaje.

Listas y tuplas.
Operadores


Los operadores son, [según 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 operador y de datos de que se trate. También las colecciones de datos admiten la acción de operadores y listas y tuplas comparten los mismos.



Los operadores disponibles en listas y tuplas son los siguientes:
  • :, que permite obtener segmentos del contenido de ambas colecciones.
  • + y *, que permiten realizar las operaciones sobre listas y tuplas. Veremos cuáles.
  • in / not in, que permiten verificar la presencia / no presencia de determinado dato dentro del conjunto.
  • Y 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 hacer con 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úa 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 datos forma parte o no de una lista o de una tupla. in y not in devuelven el booleano 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 su contenido es el mismo (valores enteros 1,2,3,4 en ambos casos)

jueves, 15 de mayo de 2025

Python. Lenguaje.

Listas y tuplas
Creación automática de contenido


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, por diferentes motivos, 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 ese proceso. 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 mucho 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, también es necesario conocer para entender de qué se trata si nos encontramos con script que utilicen estas expresiones. Me estoy refiriendo a la creación de listas o tuplas que utilizan operadores, bucles y condicionales como parte de las expresiones que utilizan para crear la lista o tupla. Veamos algunos ejemplos: 
  • La lista_3 = [x * 2 for x in lista_1] es una lista de valores numéricos que utiliza la 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 de tuplas (antes no el listas) y usamos 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)
Y 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

Python. Lenguaje.


Listas y tuplas
.

Acceso al contenido



Aunque listas y tuplas son colecciones de datos diferentes, ya dijimos que comparten muchas características y comportamientos; entre estos últimos están su 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 anidados dentro de la lista o tupla principal?.

Un ejemplo de ello podría ser el siguiente: una lista que contiene "registros" que se comportan, a su vez como listas (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 datos de la primera sublista, deberemos expresarlo como sigue: print([mi_lista_b[0][0]) (nos devuelve -> Lucia). Observa que empleamos una pareja de corchetes para identificar el registro (en este caso el primero [0]) y a continuación una segunda pareja para identificar el datos que deseamos (en este caso también el primero [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 apartado 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 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 (->4) es 4, por lo que el anterior (elemento 2 -> 3). 

Ahora bien 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 suponemos 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 da igual contenido que lista0, esto es, 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

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 algoritmo.

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 "<pyshell#2>", line 1, in <module>
    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 (en rojo) 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, 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 "<pyshell#12>", line 1, in <module> 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. 

En realidad 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 [algoritmo resultante queda aquí] a tu disposición para que lo estudies. La idea es que crees tú algún otro semejante y compares tu solución con la mía, que ya advierto 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 algoritmo de 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é.