Ámbito de variables: qué datos puede ver cada parte del código
Una llamada comprueba un ancho de 0.90 metros y devuelve verdadero. Repetimos la misma llamada y devuelve falso. No hay un modelo que se haya actualizado ni un servidor que conteste a destiempo: hemos cambiado un umbral fuera de la función. La comparación sigue haciendo su trabajo; lo que estaba incompleta era nuestra descripción de los datos que necesitaba.
El ámbito determina dónde se puede utilizar un nombre. Comprenderlo permite localizar dependencias que una llamada no muestra y distinguir una variable local de otra que se llama igual en otra parte. Vamos a trabajar con umbrales didácticos de ancho, sin valor normativo, y a conseguir que dos comprobaciones con límites distintos no se contaminen. Otra entrega sobre quién puede ver un nombre. Confío en que mi prestigio se recupere cuando terminemos de poner orden en cuatro variables.
Una entrada oculta hace que la llamada cuente media historia
En Python una función puede consultar nombres definidos en el módulo donde se declaró. Si no encuentra un nombre local, el mecanismo de resolución puede buscarlo en ámbitos exteriores según las reglas del lenguaje. Esto permite utilizar constantes o funciones compartidas, pero también introduce dependencias que no aparecen entre los argumentos de una llamada. La comodidad necesita un criterio, no una prohibición universal ni una confianza universal.
El programa siguiente guarda un umbral fuera de la función y lo consulta desde su cuerpo. Primero vale 0.80 y después 1.00. La llamada solo muestra el ancho 0.90, aunque el resultado depende de ambos números. Puedes ejecutar este archivo con Python 3.12 sin instalar librerías.
La salida es True y False. No hay aleatoriedad: en cada ejecución la comparación utiliza el umbral que encuentra al ser llamada. La función tiene una dependencia adicional a su argumento. Si alguien cambia ese estado entre dos comprobaciones, la misma expresión de llamada puede representar decisiones diferentes. El nombre cumple_ancho no avisa de quién ha tenido la última palabra sobre el límite.
Una configuración común puede tener sentido si permanece estable y se gestiona expresamente. El problema aparece cuando usamos una variable compartida como canal improvisado para pasar el criterio de una llamada a otra. Revisar un proyecto cambiando primero un valor global y después llamando a una función es una secuencia fácil de olvidar. El programa no debería exigir que cada lector conozca también el ritual previo al botón.
Un parámetro hace visible el criterio de cada comprobación
Para este caso pasaremos el umbral como argumento. La firma mostrará los dos datos necesarios: ancho y mínimo, ambos en metros. La función ya no necesita consultar el valor externo para responder. Dos llamadas pueden utilizar criterios distintos sin reconfigurar una variable compartida antes de cada una, lo que permite leer y probar cada decisión en su propio contexto.
El siguiente archivo es independiente del anterior. Conserva deliberadamente un nombre externo minimo_m con valor 9.0 para comprobar que no es el que utiliza la función. El parámetro del mismo nombre pertenece al ámbito local de la llamada; dentro de ella representa el argumento recibido.
Verás True, False, True y 9.0. El primer y el tercer resultado coinciden aunque entre ellos se haya comprobado otro umbral. La variable externa conserva su valor. resultado también es local a la función y se entrega mediante return; no necesitamos consultarla desde fuera para recibir la respuesta. Cada llamada tiene su propio contexto de ejecución, aunque reutilice los mismos nombres de parámetros.
Usar el mismo nombre permite mostrar el mecanismo, pero no obliga a repetir nombres si confunden la lectura. En una herramienta concreta podríamos llamar al parámetro ancho_minimo_m y a la configuración externa criterio_del_proyecto. Yo priorizaría que la llamada explique qué valores está utilizando. La sombra de un nombre es un comportamiento del lenguaje, no una estrategia para esconder el criterio con un poco más de sofisticación.
La explicación de ámbitos y espacios de nombres de Python detalla esa resolución. No hace falta memorizar toda su terminología para seguir este programa: dentro de la función, el parámetro recibido es el nombre que estamos consultando. Fuera, el nombre del módulo conserva su propio enlace. Cambiar uno de esos enlaces no convierte al otro en una variable compartida por casualidad.
Una asignación local puede cambiar cómo se interpreta un nombre
Leer una variable externa y asignar un nombre dentro de una función no son operaciones intercambiables. En Python, una asignación a un nombre dentro de la función hace que se trate como local en ese ámbito, salvo declaraciones como global o nonlocal. Por eso una instrucción que intente aumentar un supuesto umbral externo mediante minimo_m = minimo_m + 0.1 puede provocar UnboundLocalError: se intenta leer un nombre local antes de que tenga un valor.
La reacción rápida suele ser añadir global. Esa declaración puede ser correcta si la responsabilidad de la función es modificar estado del módulo, pero no es una reparación neutra. Cambia el contrato: la función pasa a afectar a un valor compartido que otras llamadas pueden consultar. Para una comprobación de ancho que solo debe responder verdadero o falso, añadir esa escritura sería un rodeo difícil de justificar.
En nuestro diseño no necesitamos ninguna declaración especial porque los números llegan como parámetros y el resultado vuelve mediante return. Si una operación debe proponer otro umbral, puede calcularlo y devolverlo para que el llamador decida si lo conserva. Esa decisión explícita resulta más fácil de seguir que una función que revisa una puerta y, de paso, cambia el criterio para las siguientes. Una forma bastante sutil de conseguir que la primera y la última revisión pertenezcan a proyectos distintos.
Hay otra diferencia que aparecerá al trabajar con listas y diccionarios: un nombre local puede apuntar a un objeto mutable compartido. Modificar ese objeto puede ser visible desde fuera aunque el nombre solo exista dentro de la función. Aquí usamos números y reasignaciones para aislar el problema de ámbito; el próximo artículo estudiará referencias y copias. Que una variable sea local no significa que todo lo que alcanza sea privado ni que el objeto se haya duplicado.
C# permite impedir capturas en una función local estática
En C# una función local puede utilizar variables de un ámbito que la contiene si no es estática y las reglas del lenguaje lo permiten. Eso se llama captura. Para nuestra comparación podemos prescindir de ella y declarar una función local static con ancho y mínimo como parámetros. El compilador rechazará que intentemos capturar una variable local exterior desde esa función estática.
Este programa completo para una consola de .NET 8 produce las mismas cuatro líneas que Python. La cultura invariante fija el punto decimal de la última línea para contrastar las salidas; no establece el formato de todos los informes del proyecto. El umbral externo no se utiliza en la decisión y conserva 9.0. El nombre del parámetro puede coincidir con el nombre exterior en esta función local estática sin que se conviertan en el mismo dato.
La guía de funciones locales de C# explica el ámbito de estas funciones y las capturas. static no demuestra por sí solo que una función sea pura: todavía puede llamar a otras operaciones o acceder a estado estático permitido. En este fragmento la independencia se sostiene porque el cuerpo solo compara los parámetros y devuelve el resultado. La palabra clave ayuda a impedir un error concreto, no sustituye leer lo que ejecuta.
También importa el ámbito de los bloques. En C#, una variable declarada dentro de un bloque no se puede utilizar simplemente fuera de él. Si declaramos resultado dentro del cuerpo de nuestra función, el llamador debe recibirlo mediante el retorno. Intentar leerlo directamente desde las instrucciones exteriores es un error de compilación, no una forma alternativa de recoger la respuesta.
La sangría de Python no crea el mismo ámbito que un bloque de C#
En Python, un bloque if o un bucle no crea un ámbito local nuevo como una función. Un nombre asignado durante ese recorrido puede seguir disponible después en el mismo ámbito. El siguiente programa deja ultimo_codigo con el valor de la última vuelta. La sangría decide qué instrucciones se repiten, pero no convierte cada vuelta en una función independiente.
La salida es X99. pass no realiza trabajo; permite dejar un cuerpo válido para mostrar únicamente la asignación del nombre del recorrido. Si la lista estuviera vacía y el nombre no se hubiera definido antes, no habría ninguna asignación y la consulta posterior provocaría NameError. La disponibilidad del valor dependería entonces de si el bucle llegó a ejecutarse. Yo no utilizaría ese comportamiento como método para pedir «el último resultado» sin tratar el caso vacío.
En C#, la variable declarada como parte de un foreach tiene un ámbito limitado al cuerpo de ese recorrido. Copiar la consulta posterior de Python no reproduce el mismo contrato. Estas diferencias explican por qué un programa puede fallar al trasladar código aunque los nombres y la intención parezcan iguales. Las reglas de visibilidad pertenecen al lenguaje, y el dibujo de los bloques no basta para deducirlas.
Para comprobar nuestra función de ancho, conserva la secuencia de umbrales 0.80, 1.00 y 0.80. Debe devolver verdadero, falso y verdadero sin alterar la configuración exterior. Si una llamada necesita que antes cambies una variable que no aparece en su firma, ya sabes dónde buscar la dependencia. El criterio de validación merece estar en los datos que entregamos, no en una negociación secreta entre nombres que viven en distintas habitaciones del programa.
Respuestas