Nulo, vacío y ausente: tres estados que no significan lo mismo
Una exportación no incluye la columna del código. Otra la incluye, pero deja el valor nulo. Una tercera entrega una cadena vacía. Si nuestro importador convierte las tres situaciones en «borrar el código», habrá conseguido simplificar los datos a costa de inventarse una instrucción. Es un atajo estupendo para terminar pronto una importación y dedicar el resto de la tarde a explicar por qué han desaparecido valores.
Antes de validar o actualizar un campo, necesitamos saber qué hemos recibido realmente. Vamos a distinguir presencia, valor nulo y texto vacío con registros ficticios en Python y C#. Añadiremos espacios y tipos incorrectos para que el diagnóstico no dependa únicamente del caso feliz. El programa clasificará entradas; las decisiones sobre corregir, conservar o borrar seguirán siendo explícitas. La ausencia de información no debería disfrazarse de una orden por comodidad del desarrollador.
La presencia de una clave también forma parte del dato
Un diccionario permite comprobar si una clave existe antes de leer su valor. En un registro como {}, no tenemos la clave codigo. En {"codigo": None}, la clave existe y su valor es nulo. En {"codigo": ""}, existe y contiene un texto de longitud cero. Podemos decidir tratar algunos casos de la misma forma en un proceso, pero primero debemos conservar la diferencia para poder tomar esa decisión.
La ausencia no explica por sí sola qué ocurrió en la fuente. Quizá el exportador no solicitó el campo, una versión del esquema no lo incluye o una transformación lo omitió. Un valor nulo tampoco tiene un significado universal: puede representar un dato desconocido, no informado o una instrucción concreta si el contrato lo establece. Leer el estado correctamente es el primer paso; atribuirle una causa sin información adicional sería inventar el segundo.
En Python, None representa un valor nulo convencional. No es el texto "None", ni una cadena vacía, ni el número cero. La comprobación valor is None identifica ese valor de forma explícita. Para una clave ausente utilizaremos primero "codigo" not in registro; así no perderemos la distinción al recuperar el contenido.
Este es un pequeño contrato para un campo que debe ser texto. Separa también el texto compuesto únicamente por espacios y una entrada con tipo incorrecto. La etiqueta «con contenido» solo confirma que hay texto no vacío: todavía no comprueba que el código pertenezca al catálogo del proyecto.
Puedes ejecutar el archivo con Python 3.12. Las seis líneas son ausente, nulo, vacio, solo espacios, tipo incorrecto y con contenido. El orden de las condiciones evita llamar a strip sobre un nulo o un número. Tampoco convierte ese número en texto para que entre a la fuerza: que podamos escribir str(0) no demuestra que la fuente haya entregado el código acordado.
Un valor por defecto puede borrar una diferencia que necesitabas
El método get de un diccionario resulta cómodo porque permite recuperar una clave sin provocar un error cuando no existe. Sin un valor por defecto explícito, devuelve None si la clave falta. Eso significa que puede producir el mismo resultado para una clave ausente y para otra presente cuyo valor ya era None. El método hace lo prometido; somos nosotros quienes debemos decidir si esa pérdida de distinción es aceptable.
El programa independiente imprime True, True, False y True. Si solo hubiésemos guardado las dos primeras respuestas, no podríamos distinguir qué registro contenía el campo. La referencia de diccionarios de Python define ese comportamiento. Para nuestra clasificación utilizamos presencia y valor por separado porque los dos participan en el diagnóstico.
Esto no convierte get en un método peligroso. Si una configuración opcional admite exactamente el mismo comportamiento tanto para ausencia como para nulo, puede ser apropiado. El criterio consiste en elegirlo sabiendo qué información deja de distinguir. Añadir or "" después de cada lectura para que todo se parezca a un texto vacío no es una política de datos; es una manera muy eficaz de esconder que todavía no la hemos definido.
Cuando un proceso necesita un marcador único para ausencia también puede utilizar un objeto centinela, pero aquí no hace falta introducirlo. La comprobación de pertenencia expresa directamente lo que queremos saber y mantiene el ejemplo accesible. No necesitamos una técnica más elaborada si una pregunta clara al diccionario conserva el contrato.
Cero y falso no significan que falte información
En Python, varios valores se consideran falsos al evaluarlos como condición: entre ellos None, cero, False y ciertas colecciones vacías. Por eso if not valor puede agrupar estados que necesitan diagnósticos diferentes. Puede ser útil cuando esa agrupación es deliberada, pero no permite deducir que el valor esté ausente. Una medición cero podría ser un dato presente que exige una comprobación específica.
El siguiente archivo usa dos lecturas didácticas. La primera tiene valor 0.0 y la segunda tiene valor nulo. No estamos afirmando que cero sea una medición válida en cualquier campo: estamos conservándolo para que la regla correspondiente decida, en vez de convertirlo prematuramente en «sin medición».
La salida es P01 0.0 y P02 sin medicion. El cero sobrevive porque la condición pregunta por nulo. Una regla posterior podrá rechazarlo si el dato debe ser positivo. Eso permite explicar el motivo real: «valor fuera del rango admitido» aporta una información distinta de «no hemos recibido un valor». Si ambas situaciones terminan en el mismo mensaje, quien corrige tiene que empezar investigando algo que el programa ya sabía.
Con un campo booleano ocurre algo parecido. False puede ser una respuesta completa, como «no requiere revisión», y no una celda pendiente de rellenar. Si el tipo acordado es booleano, necesitamos distinguirla de nulo. Además, aceptar booleanos como números por accidente requiere cuidado en Python porque bool es un subtipo de int. Los tipos y las reglas de cada campo deben estar definidos antes de acumular condiciones genéricas de «dato informado».
C# puede entregar presencia y valor en una misma consulta
En un diccionario de C#, TryGetValue devuelve un booleano que indica si existe la clave y entrega el valor mediante un parámetro out. Conservar ambos permite distinguir una clave ausente de otra cuyo valor es nulo. En este ejemplo utilizamos object? para representar entradas heterogéneas que todavía vamos a diagnosticar; no es una recomendación de usar ese tipo en todas las capas de una aplicación.
El programa completo para una consola de .NET 8 produce las mismas seis líneas que el primer Python. La comprobación de tipo introduce el nombre texto cuando el valor es una cadena. La llamada a IsNullOrWhiteSpace llega después de separar nulo y vacío; la utilizamos aquí para identificar el caso restante de espacios en blanco. Un método que agrupa estados puede seguir siendo útil si antes hemos conservado las distinciones que necesita el proceso.
La anotación ? permite expresar que una referencia puede ser nula dentro del análisis de nulabilidad de C#. No valida por sí sola el significado de la entrada ni demuestra que exista la clave. El programa sigue necesitando ambas preguntas. Un compilador no puede decidir si un nulo enviado por un proveedor significa «no lo sé», «no aplica» o «elimínalo»: esa conversación pertenece al contrato de intercambio.
Este contrato tampoco se traslada automáticamente a todos los valores de una API BIM. La ausencia de un parámetro en Revit, un valor no asignado y un parámetro presente con contenido requieren interpretar la API correspondiente. Nuestros diccionarios son datos ficticios para aprender la distinción; no simulan todos los estados de un elemento real. El principio reutilizable es conservar la información suficiente antes de decidir qué hacer con ella.
Validar un campo y decidir una actualización son tareas distintas
En una validación podemos considerar que ausencia, nulo y vacío requieren revisión, pero conviene conservar el diagnóstico preciso. En una actualización parcial, omitir un campo podría significar mantenerlo, mientras que un nulo explícito podría tener otro efecto si así lo define el servicio. No existe una regla universal que nos autorice a interpretar cualquier nulo como una orden de borrado. Hay que leer y probar el contrato concreto antes de escribir.
Un informe útil puede mantener el identificador del elemento, el estado detectado y una explicación suficiente para corregirlo. «Campo no recibido» invita a revisar la exportación o el esquema. «Texto vacío» dirige la atención al contenido. «Tipo incorrecto» señala una transformación o una fuente que no ha entregado lo esperado. Clasificar no obliga a corregir automáticamente, pero permite dejar de pedir a la persona que adivine la causa de una celda roja.
Prueba también la cadena "None", el texto "0", un booleano y una cadena con espacios alrededor de D01. Nuestra función clasificará los dos primeros y el último como texto con contenido, sin certificar su validez de catálogo; rechazará el booleano como tipo incorrecto. Son casos útiles para comprobar que el diagnóstico no está interpretando textos especiales como si fuesen valores del lenguaje.
Si en tu intercambio de datos un nulo significa algo concreto, explícalo en los comentarios junto con el campo y el formato. Esa precisión vale más que una receta para rellenar todos los huecos con ceros. Yo seguiré considerando un pequeño triunfo profesional que un importador sepa distinguir «no me lo han enviado» de «me han pedido que lo cambie» antes de presumir de velocidad.
Respuestas