Ilustración conceptual de un espacio agrupado con su identidad, nivel y superficie y una propuesta separada.

Tuplas y registros: agrupar valores que forman una unidad

espacio[2] contiene la superficie. Salvo que alguien haya colocado antes el nivel, en cuyo caso contiene el nivel. La instrucción es breve y el acuerdo necesario para entenderla ocupa media conversación. Me parece un intercambio poco ventajoso. Si tres datos describen el mismo espacio, vamos a guardarlos juntos y a decidir cuándo sus posiciones necesitan nombres.

Utilizaremos un espacio ficticio con identificador E01, nivel L01 y superficie de 18.5 metros cuadrados. Una tupla de Python permite agrupar esos valores; una tupla con campos nombrados de C# hace visible su significado, y un registro permite declarar una representación con nombre propio. Veremos qué protege cada forma y qué errores siguen siendo posibles. Agrupar datos ayuda a conservar su relación; no sustituye comprobarla.

Tabla de contenidos

Los datos de un espacio deberían viajar juntos

Tres listas paralelas pueden conservar identificadores, niveles y superficies. Mientras se mantengan exactamente alineadas, la posición cero de cada lista describe el mismo espacio. Si alguien ordena solo los identificadores, esa relación se rompe sin que cambie la longitud de ninguna colección. El informe puede seguir teniendo el tamaño esperado y repartir las superficies con una libertad artística que nadie había contratado.

Una tupla agrupa varios valores en una unidad. En Python podemos escribirlos separados por comas, normalmente entre paréntesis para hacer visible el grupo. No estamos creando un espacio de Revit ni leyendo sus propiedades; estamos eligiendo cómo representar una fila de datos durante el programa. La identidad, el nivel y la superficie quedan juntos cuando entregamos esa unidad a otra operación.

				
					espacio = ("E01", "L01", 18.5)
identificador, nivel, superficie_m2 = espacio
print(identificador)
print(nivel)
print(superficie_m2)
				
			

La salida es E01, L01 y 18.5, en líneas separadas. La segunda instrucción desempaqueta la tupla: asigna cada componente al nombre situado en la misma posición. El nombre superficie_m2 conserva la unidad que hemos acordado para el número. Un valor decimal no incluye automáticamente metros cuadrados; si el origen trabajase en otra unidad, la conversión tendría que ocurrir en un punto identificado del proceso.

El desempaquetado requiere que la estructura recibida corresponda a lo esperado. Con dos o cuatro componentes, esta asignación de tres nombres falla en vez de adivinar qué sobra o falta. Es una ayuda concreta, pero todavía limitada: si llegan tres componentes en otro orden, el número de valores seguirá encajando. Haber conseguido que los datos viajen juntos no garantiza que hayan subido al asiento correcto.

La posición sigue siendo un acuerdo que puede romperse

La documentación de tuplas de Python describe el agrupamiento y el desempaquetado. La sintaxis permite dar nombres locales a los componentes sin cambiar su orden. Por eso identificador, superficie_m2, nivel = espacio también se ejecutaría con la tupla anterior: asignaría el texto L01 al nombre de superficie y el número 18.5 al nombre de nivel. El nombre de una variable expresa nuestra intención; no obliga al valor a obedecerla.

Podemos detectar parte de ese intercambio comprobando el contrato. En este ejercicio el identificador y el nivel deben ser textos, y la superficie un número positivo en metros cuadrados. El fragmento siguiente tiene un error deliberado en el orden de los valores. El control muestra que la superficie no es evaluable sin intentar compararla numéricamente antes de conocer su tipo.

				
					espacio = ("E01", 18.5, "L01")
identificador, nivel, superficie_m2 = espacio
if not isinstance(superficie_m2, (int, float)) or isinstance(superficie_m2, bool):
    print("Superficie no evaluable: tipo inesperado")
else:
    print("Superficie recibida en m2:", superficie_m2)
				
			

La salida es Superficie no evaluable: tipo inesperado. Excluimos expresamente los booleanos porque en Python tienen relación con el tipo entero y no queremos admitir True como superficie. Este control no valida todavía el nivel, el identificador, valores infinitos ni todas las condiciones de una medición real. Su cometido es mostrar cómo un cambio de posición puede convertirse en un dato que no cumple el contrato previsto.

Si el grupo empieza a pasar por muchas funciones o archivos, exigir que todo el mundo recuerde esas posiciones deja de compensar. En Python existen representaciones con campos nombrados, que desarrollaremos al tratar objetos y dataclasses. No hace falta introducirlas todas aquí. La decisión útil consiste en reconocer el límite: una pareja local fácil de leer y un registro que atraviesa varias capas de una aplicación no tienen las mismas necesidades de explicación.

C# permite nombrar los campos de una tupla

En C# podemos declarar una tupla con tipos y nombres para sus componentes. El acceso espacio.SuperficieM2 comunica mejor la pregunta que Item3. La declaración conserva además la diferencia entre textos y números: asignar el texto L01 al componente declarado como double no es una conversión implícita válida. Eso evita ciertos intercambios, aunque dos campos de texto todavía puedan ocupar posiciones equivocadas.

				
					using System;
using System.Globalization;

(string Id, string Nivel, double SuperficieM2) espacio = ("E01", "L01", 18.5);
Console.WriteLine(espacio.Id);
Console.WriteLine(espacio.Nivel);
Console.WriteLine(espacio.SuperficieM2.ToString(CultureInfo.InvariantCulture));

var copia = espacio;
copia.Nivel = "L02";
Console.WriteLine(espacio.Nivel);
Console.WriteLine(copia.Nivel);
				
			

Este programa de consola para .NET 8 imprime E01, L01, 18.5, L01 y L02. La cultura invariante fija el punto decimal de esta salida para poder compararla con el ejemplo Python; no es una recomendación para presentar cualquier informe en España. Después copiamos la tupla y cambiamos el nivel de la copia. Las tuplas de C# son tipos de valor mutables: la modificación de ese campo en la copia no cambia el campo correspondiente de la variable original.

La referencia de tuplas de C# documenta tanto los campos nombrados como esa semántica de valor. Los nombres ayudan a leer y escribir el código, pero no deben tratarse como una validación del significado. Dos campos declarados como string pueden intercambiarse y seguir teniendo tipos compatibles. El compilador puede detectar una incompatibilidad de tipos; no sabe si el texto que le entregamos representa un identificador de espacio o un nivel del proyecto.

Tampoco conviene trasladar automáticamente el comportamiento de copia a todo lo que contenga una tupla. Si un componente refiere a una lista mutable, copiar el grupo puede conservar la referencia a esa lista. Volvemos a la misma distinción que vimos en colecciones: copiar el recipiente no implica duplicar profundamente los objetos a los que apunta. Las representaciones se entienden mejor cuando se describe qué cambia y qué sigue compartido, en lugar de repartir la etiqueta «copia» como una garantía universal.

Un registro pone nombre a una representación reutilizable

Cuando el mismo grupo de datos aparece en muchas partes del programa, darle un nombre de tipo permite expresar que esperamos un Espacio, no cualquier terna de valores. En C# utilizaremos un record posicional con identificador, nivel y superficie. La declaración genera propiedades y comportamiento de igualdad basado en los valores correspondientes de sus miembros, entre otras funciones. No es simplemente otra forma de escribir una tupla.

				
					using System;

var original = new Espacio("E01", "L01", 18.5);
var propuesta = original with { Nivel = "L02" };
Console.WriteLine(original.Nivel);
Console.WriteLine(propuesta.Nivel);
Console.WriteLine(original == new Espacio("E01", "L01", 18.5));

public record Espacio(string Id, string Nivel, double SuperficieM2);
				
			

Este programa independiente para .NET 8 muestra L01, L02 y True. with crea otra instancia a partir del registro y permite cambiar las propiedades indicadas en esa nueva representación. La variable original conserva su nivel. La igualdad del último mensaje compara dos instancias del mismo tipo de registro con los mismos valores de texto y número; no demuestra que representen el mismo elemento real de un modelo ni que su superficie sea correcta.

La referencia de registros de C# explica las propiedades generadas y sus variantes. Aquí record define un tipo de referencia; record struct tiene otra semántica que no necesitamos introducir para este caso. En este registro posicional las propiedades generadas admiten inicialización, pero no una asignación ordinaria posterior como la que hicimos con la tupla mutable. La propuesta se expresa creando otro valor de datos mediante with.

Dar un nombre al tipo no añade por sí solo reglas como «la superficie debe ser positiva». La declaración admite que construyamos un Espacio con nivel vacío o una cifra negativa si no incorporamos controles. Yo utilizaría el registro para hacer visible la forma que circula por el programa y decidiría por separado dónde se valida. La palabra record no es un sello de conformidad urbanística, por mucho que algunas declaraciones de tipos se presenten con esa solemnidad.

Inmutable no significa que todo lo que contiene esté congelado

La tupla de Python no permite reemplazar sus posiciones después de crearla. Sin embargo, puede contener un objeto mutable. En el fragmento siguiente el segundo componente es una lista de etiquetas; no sustituimos ese componente, sino que modificamos la lista que ya está guardada allí. El contenedor sigue teniendo las mismas dos referencias y la lista tiene ahora dos etiquetas.

				
					espacio = ("E01", ["revisar"])
espacio[1].append("sin medicion")
print(", ".join(espacio[1]))
				
			

La salida es revisar, sin medicion. En cambio, intentar sustituir espacio[0] provoca TypeError. Ese es el alcance concreto de la inmutabilidad de esta tupla. Un registro de C# que contenga una lista también necesita estudiar esa referencia y sus operaciones; no basta con que las propiedades generadas no puedan reasignarse mediante una asignación ordinaria para declarar inmutable todo el objeto de forma profunda.

En nuestro primer espacio solo utilizamos textos y un número, lo que mantiene pequeña la discusión. Si añadimos colecciones, objetos del modelo o estructuras con comportamiento propio, habrá que revisar quién puede modificarlos. Esa conversación pertenece al diseño de los datos y sus límites, no a una lucha religiosa entre tuplas y registros. Ambas formas pueden ser útiles cuando el grupo y su recorrido son comprensibles.

Antes de elegir, intenta leer el dato sin conocer cómo se creó. Si dato[2] exige buscar una convención en otra página, un nombre de campo empieza a ser una inversión razonable. Si una pareja se crea y se desempaqueta en el mismo bloque, quizá no necesite una jerarquía de tipos para anunciar su existencia. Conserva juntos identificador, nivel y superficie, deja visible la unidad y comprueba el orden al recibirlos. El ahorro de caracteres no merece que una planta termine convertida en metros cuadrados con plena aprobación sintáctica.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *