Breve descripción de las Reglas de Alcance?


¿Qué exactamente son las reglas de alcance de Python?

Si tengo algún código:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

¿Dónde se encuentra x? Algunas opciones posibles incluyen la lista anterior:

  1. En el archivo fuente adjunto
  2. En el espacio de nombres de clase
  3. En la definición de la función
  4. En la variable de índice de bucle for
  5. Dentro del bucle for

También está el contexto durante la ejecución, cuando la función spam se pasa a otro lugar. Y tal vez las funciones lambda pasan un poco diferente?

Debe haber una simple referencia o algoritmo en alguna parte. Es un mundo confuso para los programadores intermedios de Python.

Author: martineau, 2008-11-15

7 answers

En realidad, una regla concisa para la resolución del alcance de Python, de Learning Python, 3rd. Eréctil.. (Estas reglas son específicas para nombres de variables, no atributos. Si lo hace referencia sin un período, se aplican estas reglas)

Regla LEGB.

L , Nombres locales asignados de cualquier manera dentro de una función (def o lambda)), y no declarados globales en esa función.

E , Encerrando - function locals-Name en el ámbito local de any and all estáticamente encerrando funciones (def o lambda), de interior a exterior.

G , Global (module) - Nombres asignados en el nivel superior de un archivo de módulo, o ejecutando una instrucción global en un def dentro del archivo.

B, Built-in (Python) - Nombres preasignados en el módulo built-in names : open,range,SyntaxError,...

Así, en el caso de

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

El bucle for no tiene su propio espacio de nombres. En el orden LEGB, los alcances serían

L: local, en def spam (en code3, code 4, code5).

E: Función encerrada, cualquier función encerradora (si el ejemplo completo estuviera en otro def)

G : Global. ¿Hubo algún x declarado globalmente en el módulo (code1)?

B : Cualquier builtin x en Python.

x nunca se encontrará en code2 (incluso en los casos en que usted podría esperar que lo haría, ver Respuesta de Antti o aquí).

 357
Author: Rizwan Kassim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:41

Esencialmente, lo único en Python que introduce un nuevo ámbito es una definición de función. Las clases son un caso especial en el que cualquier cosa definida directamente en el cuerpo se coloca en el espacio de nombres de la clase, pero no son directamente accesibles desde los métodos (o clases anidadas) que contienen.

En su ejemplo solo hay 3 ámbitos donde se buscará x:

  • Ámbito de spam-contiene todo lo definido en code3 y code5 (así como code4, su variable de bucle)

  • El ámbito global-que contiene todo lo definido en code1, así como Foo (y cualquier cambio después de él)

  • El espacio de nombres builtins. Un caso especial-este contiene varias funciones y tipos integrados en Python como len () y str (). Generalmente esto no debe ser modificado por ningún código de usuario, así que espere que contenga las funciones estándar y nada más.

Solo aparecen más ámbitos cuando se introduce un función anidada (o lambda) en la imagen. Sin embargo, estos se comportarán más o menos como esperarías. La función anidada puede acceder a todo en el ámbito local, así como a cualquier cosa en el ámbito de la función que encierra. eg.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

Restricciones:

Se puede acceder a variables en ámbitos distintos de las variables de la función local, pero no se pueden rebotar a nuevos parámetros sin más sintaxis. En su lugar, la asignación creará una nueva variable local en lugar de afecta a la variable en el ámbito padre. Por ejemplo:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Para modificar realmente las vinculaciones de variables globales dentro de un ámbito de función, debe especificar que la variable es global con la palabra clave global. Eg:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

Actualmente no hay forma de hacer lo mismo para las variables que encierran los ámbitos function, pero Python 3 introduce una nueva palabra clave, "nonlocal" que actuará de manera similar a global, pero para ámbitos de funciones anidadas.

 138
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-15 21:51:09

No hubo una respuesta completa con respecto al tiempo de Python3, así que hice una respuesta aquí.

Como se indica en otras respuestas, hay 4 ámbitos básicos, el LEGB, para Local, Enclosing, Global y Builtin. Además de esos, hay un ámbito especial, el cuerpo de la clase, que no constituyen un ámbito envolvente para los métodos definidos dentro de la clase; cualquier tarea dentro del cuerpo de la clase que la variable a partir de ahí será atado en el cuerpo de la clase.

, Especialmente, no la instrucción block, además de def y class, crea un ámbito variable. En Python 2 la comprensión de lista no crea un ámbito variable, sin embargo en Python 3 la variable de bucle se crea en un nuevo ámbito.

Para demostrar las peculiaridades del cuerpo de clase

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable 
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Por lo tanto, a diferencia de en el cuerpo de la función, puede reasignar la variable al mismo nombre en el cuerpo de la clase, para obtener una variable de clase con el mismo nombre; las búsquedas adicionales en este nombre se resuelven a la variable clase en su lugar.


Una de las mayores sorpresas para muchos recién llegados a Python es que un bucle for no crea un ámbito variable. En Python 2 las comprensiones de lista tampoco crean un alcance (¡mientras que los generadores y las comprensiones dict sí lo hacen!) En su lugar filtran el valor en la función o el ámbito global:

>>> [ i for i in range(5) ]
>>> i
4

Las comprensiones se pueden usar como una forma astuta (o horrible si se quiere) de hacer variables modificables dentro de expresiones lambda en Python 2 - una expresión lambda crea un ámbito variable, como lo haría la sentencia def, pero dentro de lambda no se permiten sentencias. Assignment al ser una instrucción en Python significa que no se permiten asignaciones de variables en lambda, pero una comprensión de lista es una expresión...

Este comportamiento ha sido corregido en Python 3 - no hay expresiones de comprensión o variables de fuga de generadores.


El global realmente significa el alcance del módulo; el módulo principal de python es el __main__; todos los módulos importados son accesible a través de la variable sys.modules; para obtener acceso a __main__ se puede usar sys.modules['__main__'], o import __main__; es perfectamente aceptable acceder y asignar atributos allí; se mostrarán como variables en el ámbito global del módulo principal.


Si alguna vez se asigna un nombre en el ámbito actual (excepto en el ámbito de clase), se considerará que pertenece a ese ámbito, de lo contrario se considerará que pertenece a cualquier ámbito adjunto que asigne a la variable (podría no ser asignado todavía, o no en absoluto), o finalmente el alcance global. Si la variable se considera local, pero aún no está establecida, o se ha eliminado, leer el valor de la variable dará como resultado UnboundLocalError, que es una subclase de NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

El ámbito puede declarar que desea modificar explícitamente la variable global (ámbito del módulo), con la palabra clave global:

x = 5
def foobar():
    global x
    print(x) # -> 5
    x += 1

foobar()
print(x) # -> 6

Esto también es posible incluso si fue sombreado en el ámbito de inclusión:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> print 5 911
print(x, y) # -> 6 13

En python 2 no hay una manera fácil para modificar el valor en el ámbito de inclusión; por lo general, esto se simula teniendo un valor mutable, como una lista con una longitud de 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

Sin embargo, en python 3, el nonlocal viene a rescatar:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

Cualquier variable que no se considere local al ámbito actual, o cualquier ámbito que encierre, es una variable global. Un nombre global se busca en el diccionario global del módulo; si no se encuentra, el global se busca desde el módulo builtins; el nombre del módulo fue cambiado de python 2 a python 3; en python 2 era __builtin__ y en python 3 ahora se llama builtins. Si asigna a un atributo del módulo builtins, será visible a partir de entonces para cualquier módulo como una variable global legible, a menos que ese módulo los sombree con su propia variable global con el mismo nombre.


La lectura del módulo incorporado también puede ser útil; supongamos que desea la función de impresión de estilo python 3 en algunas partes del archivo, pero otras partes del archivo todavía utilizan la función print sentencia, si su versión de python es >= 2.6, puede obtener la nueva función de estilo como:

import __builtin__

print3 = __builtin__.__dict__['print']

La from __future__ import print_function en realidad no importa la función print en ninguna parte de Python 2 - en su lugar, solo deshabilita las reglas de análisis para la instrucción print en el módulo actual, manejando print como cualquier otro identificador de variable, y permitiendo así que la print la función se busque en los builtins.

 87
Author: Antti Haapala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-22 08:31:51

Las reglas de alcance para Python 2.x ya se han esbozado en otras respuestas. Lo único que añadiría es que en Python 3.0, también existe el concepto de un ámbito no local (indicado por la palabra clave 'nonlocal'). Esto le permite acceder a los ámbitos externos directamente, y abre la posibilidad de hacer algunos trucos aseados, incluyendo cierres léxicos (sin trucos feos que involucran objetos mutables).

EDITAR: Aquí está el PEP con más información sobre esto.

 20
Author: Jeremy Cantrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-15 18:52:49

Un ejemplo un poco más completo de alcance:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

Salida:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200
 20
Author: brianray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-20 04:04:39

Python resuelve sus variables con generally generalmente three tres espacios de nombres disponibles.

En cualquier momento durante la ejecución, hay son al menos tres ámbitos anidados cuya los espacios de nombres son accesibles directamente: el alcance más interno, que se busca primero, contiene los nombres locales; el espacios de nombres de cualquier función envolvente, que se buscan a partir de la más cercano que encierra el alcance; el medio ámbito, buscado a continuación, contiene el los nombres globales del módulo actual; y el el ámbito más externo (el último buscado) es el espacio de nombres que contiene nombres incorporados.

Hay dos funciones: globals y locals que le muestran el contenido de dos de estos espacios de nombres.

Los espacios de nombres son creados por paquetes, módulos, clases, construcción de objetos y funciones. No hay otros tipos de espacios de nombres.

En este caso, la llamada a una función llamada x debe resolverse en el espacio de nombres local o en el espacio de nombres global.

Local en este caso, es el cuerpo de la función método Foo.spam.

Global es global bueno global global.

La regla es buscar los espacios locales anidados creados por las funciones de método (y las definiciones de función anidadas), luego buscar global. Eso es.

No hay otros ámbitos. La instrucción for (y otras instrucciones compuestas como if y try) no crean nuevos ámbitos anidados. Solo definiciones (paquetes, módulos, funciones, clases e instancias de objetos).)

Dentro una definición de clase, los nombres son parte del espacio de nombres de clase. code2, por ejemplo, debe ser calificado por el nombre de la clase. Generalmente Foo.code2. Sin embargo, self.code2 también funcionará porque los objetos Python miran la clase contenedora como una alternativa.

Un objeto (una instancia de una clase) tiene variables de instancia. Estos nombres están en el espacio de nombres del objeto. Deben ser calificados por el objeto. (variable.instance.)

Desde dentro de un método de clase, tienes locales y globales. Usted dice self.variable a elija la instancia como el espacio de nombres. Notará que self es un argumento para cada función miembro de la clase, haciéndola parte del espacio de nombres local.

Ver Reglas de ámbito de Python, Ámbito de Python, Variable Scope .

 11
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:44

¿Dónde se encuentra x?

X no se encuentra porque no la has definido. :- ) Se puede encontrar en code1 (global) o code3 (local) si lo pones allí.

Code2 (los miembros de la clase) no son visibles para el código dentro de los métodos de la misma clase - normalmente accedería a ellos usando self. code4 / code5 (bucles) viven en el mismo ámbito que code3, por lo que si escribiera a x allí estaría cambiando la instancia x definida en code3, no creando una nueva x.

Python es estático scoped, por lo que si pasa 'spam' a otra función spam todavía tendrá acceso a los globales en el módulo de donde proviene (definido en code1), y cualquier otro ámbito que contenga (ver más abajo). los miembros de code2 volverían a ser accedidos a través de self.

Lambda no es diferente a def. Si tiene una lambda usada dentro de una función, es lo mismo que definir una función anidada. En Python 2.2 en adelante, los ámbitos anidados están disponibles. En este caso, puede enlazar x en cualquier nivel de anidamiento de funciones y Python seleccionará hasta la instancia más interna:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

Fun3 ve la instancia x desde el ámbito de contenido más cercano, que es el ámbito de función asociado con fun2. Pero las otras instancias x, definidas en fun1 y globalmente, no se ven afectadas.

Antes de nested_scopes-en Python pre-2.1, y en 2.1 a menos que específicamente solicite la característica usando un from-future - import-fun1 y los ámbitos de fun2 no son visibles para fun3, por lo que la respuesta de S. Lott se mantiene y obtendría la x global:

0 0
 7
Author: bobince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-15 12:44:59