Saltar al contenido principal
LibreTexts Español

2.10: Variables y Alcance

  • Page ID
    55115
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    En el capítulo 18, “Funciones de Python”, aprendimos que una función debe usar solo variables que hayan sido definidas dentro del bloque de la función, o pasadas explícitamente como parámetros (Regla 1). Esta regla trabaja en estrecha colaboración con nuestra definición de una variable en Python, como un nombre que hace referencia a algunos datos.

    Este es un concepto potente, que nos permite pasar parámetros a funciones y obtener valores devueltos. En Python, una sola pieza de datos puede ser referenciada por múltiples variables. Las variables de parámetro son nuevos nombres para los mismos datos. Podemos explorar este concepto con un ejemplo sencillo.

    II.10_1_py_99_7_vars_refs_ex

    En esta muestra de código, hemos creado una sola lista, pero estamos asociando con ella dos nombres de variables, nums y numsb. Cuando modificamos la lista a través de un nombre, el cambio se refleja a través del otro (solo podemos cambiar los datos porque las listas son mutables, pero incluso los datos inmutables pueden ser referidos por múltiples nombres).

    Lo mismo sucede cuando usamos un parámetro en una función. Volvamos a visitar la función gc_content (), que hace uso de una función base_composition () que escribimos (pero dejar fuera aquí para mayor claridad). En este listado, todos los nombres de variables que pertenecen a la función están resaltados en rojo, mientras que otros nombres de variables “globales” se resaltan en púrpura y subrayados con una línea discontinua. De acuerdo con la Regla 1, no deberíamos ver ninguna variable morada/subrayada en la definición de la función.

    alt
    alt

    Fuera de la función, los datos de cadena “ACCCTAGACTG” se asignaron a la variable seq3, pero dentro de la función, se asignó a la variable seq. Dentro de la función, el valor de datos 0.54 fue referenciado por la variable gc, y afuera fue referenciado por seq3_gc. [1]

    Sabemos que podríamos acceder a la variable seq3 desde dentro de la función, aunque no debiéramos Pero, ¿y al revés? ¿Podríamos acceder a la variable gc, que se definió dentro de la función, fuera de la función?

    alt

    De hecho, no podemos. La línea anterior print (gc), incluso después del cálculo de seq3_gc, produciría un nameError: name 'gc' no está definido. Este error se produce porque las variables tienen alcance, y el alcance de la variable gc es limitado, desde el momento en que se define hasta el final del bloque de función en el que se define (todas las variables resaltadas en rojo comparten esta característica). Las variables con alcance limitado se denominan variables locales. El alcance de una variable es el contexto en el que se puede usar. Define “cuánto tiempo vive la variable” o “dónde existe la variable”. Una variable con alcance limitado, como en la ejecución de una función, se denomina variable local.

    Después de que termine la llamada a la función, la imagen se vería más así:

    alt

    Eventualmente, el recolector de basura notará que ninguna variable se refiere a los datos 2, 4 y 11, y los limpiará de la RAM para dejar espacio para nuevos datos.

    Debido a que scope habla de variables, poco tiene que ver con los datos a los que hace referencia una variable, excepto decir que los datos son basura recolectados cuando no hay variables que se refieran a esos datos en ningún ámbito actual. Sin embargo, debido a que la variable gc no está en el alcance después de que termina la función, no podemos usarla fuera del bloque de función.

    Una de las características particularmente interesantes y poderosas de las variables locales es que existen independientemente de cualquier otra variable que pueda existir en ese momento con el mismo nombre (a veces se dice que “sombrean” otras variables con el mismo nombre). Considera el siguiente código:

    alt

    ¿Qué opinas que imprimirá la última línea, print (gc)? Si adivinaste “Medida de bases G y C en una secuencia”, ¡tienes razón! Aunque la función gc_content () define su propia variable gc, debido a que esta variable es local, no “pisa” la variable gc externa. En resumen:

    • Las variables definidas dentro de una función (y los parámetros de una función) son locales a la llamada a la función.
    • Estas variables locales “sombrean” cualquier otra variable que pueda existir durante la ejecución de la función.
    • Cuando termina la llamada a la función, se olvidan las variables locales.

    Una de las lecciones importantes del alcance es saber cuándo podría generar problemas. Esto es más fácil de ver si fuéramos a comparar cómo funciona el scoping con otro lenguaje, C++ en este caso. En C++, las variables se declaran con sus tipos, y las variables declaradas dentro de un bloque de sentencia if (o cualquier otro bloque) también son locales a ese bloque. Esto significa que se van fuera de alcance cuando termina el bloque. Si tratáramos de utilizarlos fuera del bloque en el que fueron declarados, se produciría un error.

    En Python, por otro lado, las variables declaradas en sentencias if, bloques for-loop y bloques while loop no son variables locales, y permanecen en el alcance fuera del bloque. Así decimos que C++ tiene alcance de “nivel de bloque”, mientras que Python usa solo el alcance de “nivel de función”. Los corchetes en la siguiente figura ilustran el alcance de algunas variables en código equivalente de C++ y Python. Debido a que C++ usa alcance a nivel de bloque, y x e y se declaran dentro de bloques, no podemos acceder a su contenido fuera de esos bloques.

    II.10_7_amboth_scope

    Esto puede parecer una ventaja para Python, pero en este caso pensamos que ese C++ puede tenerlo bien. Después de todo, si necesitábamos que las variables y y z fueran accesibles después de los if-blocks en el ejemplo de C++, podríamos haberlas declarado junto con x. Incluso podríamos haberles dado un valor predeterminado para mantener usando declaraciones como int y = -1 e int z = -1; si estas variables aún mantienen -1 después de ejecutar los if-blocks, entonces sabemos que esas líneas internas no se ejecutaron. Esto sería especialmente útil si el valor de x no se fijara en cinco como está aquí, sino que depende de los datos de entrada.

    En Python, por otro lado, código como este podría causar un problema. Si la variable x depende de los datos de entrada, entonces es posible que las variables y y z nunca se establezcan en absoluto, lo que lleva a un eventual NameError cuando posteriormente se accede a ellas. Ciertamente no deberíamos considerar que ningún programa que a veces produzca un error, dependiendo de los datos de entrada, ¡es muy bueno!

    Probablemente la mejor manera de evitar este tipo de problema es pretender que Python usa alcance a nivel de bloque, declarando variables en el bloque/nivel más amplio en el que se usarán. Ninguno es un buen valor predeterminado que luego podemos verificar si queremos ejecutar condicionalmente código posterior sobre la base de si las variables realmente se establecieron en algo útil:

    II.10_8_PY_103_Ámbito Robusto

    Este código no se bloqueará, sin importar el valor de x. Establecer los valores iniciales para estas variables en Ninguno antes de los if-blocks también proporciona un recordatorio y una pista visual de que estas variables están destinadas a ser utilizadas fuera de los bloques if anidados, y que debemos verificar su contenido antes de usarlas más tarde.

    C++ y otros lenguajes de ámbito de nivel de bloque fomentan así las variables de “corta duración”, lo cual es un buen mantra de programación. Definir variables solo cuando sea necesario y usarlas por el menor tiempo posible ayuda a producir código más claro, más modular e incluso más eficiente (porque las variables de corta duración permiten que el recolector de basura limpie más datos). En cierto modo, romper la Regla 1 de funciones es un tipo similar de abuso de alcance variable.

    II.10_9_Espaguetis

    A los programadores principiantes a menudo les resulta más fácil evitar estas convenciones estableciendo una gran cantidad de variables cerca del inicio de un programa y luego accediendo y configurándolas en varios puntos a lo largo del programa. Esto a veces se llama coloquialmente “código de espagueti”, porque las conexiones de larga distancia creadas entre las diferentes áreas de código se asemejan a espaguetis Sin embargo, rara vez esta estrategia da sus frutos.

    Como se discute en el capítulo 25, “Algoritmos y estructuras de datos”, las variables locales y conceptos similares nos permiten resolver problemas complejos de maneras fascinantes y elegantes.

    Ejercicios

    1. Cree un diagrama RAM/variable que ilustre el siguiente segmento de código, y utilícelo para explicar la salida impresa. II.10_10_py_103_2_ram_diagram_ex1
    2. Cree un diagrama RAM/variable que ilustre el siguiente segmento de código, y utilícelo para explicar la salida impresa. II.10_11_py_103_3_ram_diagram_ex2
    3. Paso a través del siguiente código, y crear un diagrama RAM/variable para cada vez que se alcanza un “#point”. (Debido a que #point 1 se alcanza dos veces, eso significará cuatro diagramas.) Se puede suponer que los datos nunca se recolectan basura, sino que las variables locales sí desaparecen. II.10_12_py_103_4_ram_diagram_ex3
    4. ¿Qué devuelve una función en Python si no se especifica ninguna instrucción return?

    1. Muchos lenguajes funcionan como Python y permiten que diferentes variables se refieran a los mismos datos, pero en algunos lenguajes esto es imposible o menos común. En algunos idiomas, todos los datos son inmutables, lo que hace que el punto sea discutible. R es un ejemplo donde la mayoría de las variables terminan refiriéndose a datos únicos; vea el recuadro en el capítulo 29, “Funciones R”, para una comparación más detallada.

    This page titled 2.10: Variables y Alcance is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Shawn T. O’Neil (OSU Press) .