范围规则的简短说明?

Python范围规则究竟是什么?

如果我有一些代码:

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

xfind了哪里? 一些可能的select包括上面的列表:

  1. 在封闭的源文件中
  2. 在类名称空间中
  3. 在函数定义中
  4. 在for循环索引variables中
  5. 在for循环中

在执行过程中还有上下文,当function垃圾邮件通过其他地方。 也许lambda函数有点不同?

某处必须有一个简单的参考或algorithm。 这对中级Python程序员来说是一个混乱的世界。

实际上,从学习Python第3版开始 ,Python范围解决scheme就是一个简明的规则。 埃德。 。 (这些规则是特定于variables名称的,而不是属性的,如果没有句点引用,则适用这些规则)

LEGB规则。

L ,Local – 在函数中以任何方式分配的名称( deflambda )),而不是在该函数中声明为全局的。

E ,Enclosing-function locals – 在任何和所有静态封闭函数( deflambda )的本地范围内,从内部到外部的名称。

G ,Global(模块) – 在模块文件的顶层分配的名称,或者通过在文件中执行global语句。

B ,内置(Python) – 内置名称模块中预先指定的名称: openrangeSyntaxError ,…

所以,在的情况下

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

for循环没有自己的名字空间。 在LEGB订单中,范围将是

L:本地def spamcode 4code 4code 4 )。

E:封闭函数,任何封闭函数(如果整个例子在另一个def

G:全球。 在模块( code1 )中是否有全局声明?

B:Python中的任何内buildx

x将永远不会在代码2中find(即使在你可能期望的情况下,请参阅Antti的答案或在这里 )。

从本质上讲,引入新范围的Python中唯一的东西就是函数定义。 类有点特殊之处在于,直接在主体中定义的任何东西都放在类的名字空间中,但是它们不能从它们包含的方法(或嵌套类)中直接访问。

在你的例子中,只有3个范围,其中x将被search:

  • 垃圾邮件的范围 – 包含在code3和code5中定义的所有内容(以及代码4,循环variables)

  • 全局范围 – 包含code1中定义的所有内容,以及Foo(以及其后的任何更改)

  • 内置的命名空间。 有一点特殊情况 – 包含各种Python内置函数和types,如len()和str()。 通常这不应该由任何用户代码修改,所以期望它包含标准function,没有别的。

只有在向图片中引入嵌套函数(或lambda)时,才会出现更多范围。 这些将会像你所期望的那样performance得非常好。 嵌套函数可以访问本地范围内的所有内容以及封闭函数范围内的任何内容。 例如。

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

限制:

可以访问除局部函数variables以外的variables,但不能在没有进一步语法的情况下重新引用新的参数。 相反,赋值将创build一个新的局部variables,而不是影响父范围中的variables。 例如:

 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 

为了实际修改函数作用域内全局variables的绑定,您需要使用global关键字指定variables是全局variables。 例如:

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

目前在围绕函数作用域的variables中没有办法做到这一点,但是Python 3引入了一个新的关键字“ nonlocal ”,它将以类似于全局的方式工作,但对于嵌套的函数作用域。

关于Python3的时间还没有彻底的回答,所以我在这里做了个答案。

正如其他答案所提供的,LEGB有4个基本范围,即Local,Enclosed,Global和Builtin。 除此之外,还有一个特殊的范围,即class级机构 ,它不包括在class级内定义的方法的封闭范围; 类体内的任何赋值都使得从那里的variables被绑定在类体中。

尤其是除了defclass之外, 没有任何 block语句会创build一个variables作用域。 在Python 2中,列表理解不会创buildvariables作用域,但是在Python 3中,循环variables是在新的作用域中创build的。

展示class主体的特点

 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) 

因此,与函数体不同的是,您可以将variables重新分配给类体中的相同名称,以获得具有相同名称的类variables; 对这个名字的进一步的查找可以parsing为类variables。


对于许多Python新手来说,更大的惊喜之一是for循环不会创buildvariables作用域。 在Python 2中,列表推导不会创build一个作用域(虽然生成器和字典理解是这样做的!)相反,它们会泄漏函数或全局作用域中的值:

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

理解可以作为一种狡猾(或可怕的,如果你会的话)在Python 2中的lambdaexpression式中作出可修改的variables – 一个lambdaexpression式创build一个variables范围,就像def语句一样,但是在lambda中不允许任何语句。 赋值是Python中的一个语句,意味着在lambda中不允许variables赋值,但列表理解是一个expression式。

这个行为已经在Python 3中修复了 – 没有理解expression式或者生成器泄漏variables。


全球真的意味着模块范围; 主要的python模块是__main__ ; 所有导入的模块都可以通过sys.modulesvariables访问; 访问__main__可以使用sys.modules['__main__'] ,或者import __main__ ; 在那里访问和分配属性是完全可以接受的; 它们将在主模块的全局范围内显示为variables。


如果在当前范围内(除了在类范围内)分配一个名称,它将被视为属于该范围,否则将被视为属于任何分配给variables的封闭范围(可能不会被分配还是根本没有),或者最后是全球范围。 如果该variables被认为是本地的,但是它还没有被设置,或者被删除了,那么读取这个variables值将会导致UnboundLocalError ,它是NameError一个子类。

 x = 5 def foobar() print(x) # UnboundLocalError! x += 1 # assignment here makes xa local variable! 

范围可以声明它明确地想要用全局关键字来修改全局(模块范围)variables:

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

即使它被隐藏在范围内,这也是可能的:

 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 

在Python 2中,没有简单的方法来修改封闭范围中的值; 通常这是通过具有可变值来模拟的,例如长度为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 

然而在Python 3中, nonlocal来救援:

 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. 

任何不被视为局部于当前范围的variables或任何封闭范围都是全局variables。 全局名称在模块全局字典中查找; 如果没有find,则从内置模块中查找全局; 该模块的名称已从python 2更改为python 3; 在python 2中是__builtin__而在python 3中它现在被称为builtins 。 如果赋值给builtins模块的一个属性,那么之后它将作为一个可读的全局variables在任何模块中可见,除非该模块使用它自己的具有相同名称的全局variables来隐藏它们。


读取内置模块也是有用的; 假设你想在文件的某些部分使用python 3风格的打印函数,但是其他部分的文件仍然使用print语句,如果你的python版本是> = 2.6,你可以得到新的风格函数:

 import __builtin__ print3 = __builtin__.__dict__['print'] 

from __future__ import print_function实际上并不是在Python 2中的任何地方导入print函数,而是仅仅禁用当前模块中的print语句的parsing规则,像其他variables标识符一样处理print ,从而允许print函数被查找在内部。

其他答案已经概述了Python 2.x的范围规则。 我唯一要补充的是,在Python 3.0中,还有一个非本地范围的概念(由“nonlocal”关键字表示)。 这允许你直接访问外部范围,并打开一些巧妙的技巧,包括词法closures(没有丑陋的黑客涉及可变对象)的能力。

编辑:这是PEP与这个更多的信息。

范围的一个稍微更完整的例子:

 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 z = x def method(self): print "4. Enclosed self.x", self.x print "5. Global x", x try: print y except NameError, e: print "6.", e def method_local_ref(self): try: print x except UnboundLocalError, e: print "7.", e x = 200 print "8. Local x", x inst = Test() inst.method() inst.method_local_ref() 

输出:

  1. 全球x:100
  2. 封闭的y:100
  3. 封闭x:101
  4. 封闭的自我
  5. 全球x 100
  6. 全局名称“y”未定义
  7. 赋值之前引用的局部variables“x”
  8. 本地x 200

Python通常用三个命名空间来parsing你的variables。

在执行过程中的任何时候,至less有三个嵌套的作用域可以直接访问它们:最先search的最内层作用域包含本地名称; 任何封闭函数的名字空间,从最近的封闭范围开始search; 接下来search的中间范围包含当前模块的全局名称; 而最外面的范围(最后search)是包含内置名称的名称空间。

有两个函数: globalslocals globals ,向您显示这些命名空间中的两个内容。

命名空间由包,模块,类,对象构造和函数创build。 没有任何其他风格的命名空间。

在这种情况下,对名为x的函数的调用必须在本地名称空间或全局名称空间中parsing。

在这种情况下,Local是函数Foo.spam

全球是全球性的。

规则是search由方法函数(和嵌套函数定义)创build的嵌套局部空间,然后search全局。 而已。

没有其他的范围。 for语句(以及iftry等其他复合语句)不会创build新的嵌套作用域。 只有定义(包,模块,函数,类和对象实例)。

在类定义中,名称是类名称空间的一部分。 code2 ,例如,必须由类名限定。 一般来说Foo.code2 。 但是, self.code2也可以工作,因为Python对象将包含的类视为后退类。

一个对象(一个类的实例)有实例variables。 这些名字在对象的名字空间中。 他们必须由对象限定。 ( variable.instance 。)

从类方法中,你有本地和全局。 你说self.variableselect实例作为命名空间。 你会注意到self是每个类成员函数的一个参数,使它成为本地命名空间的一部分。

请参阅Python范围规则 , Python范围 , variables范围 。

xfind了哪里?

x没有find,因为你还没有定义它。 :-)如果你把它放在code1(global)或code3(local)中,你可以find它。

代码2(类成员)不可见代码在同一类的方法 – 你通常会使用自己访问它们。 code4 / code5(loops)与code3在同一个作用域,所以如果你在那里写了x,你将会改变在code3中定义的x实例,而不是创build一个新的x。

Python是静态范围的,所以如果你将垃圾邮件传递给另一个函数,垃圾邮件仍然可以访问它所来自的模块(在code1中定义)中的全局variables,以及任何其他包含范围(见下文)。 code2成员将再次通过自我访问。

lambda与def没有区别。 如果在函数内部使用了lambda,则与定义嵌套函数相同。 在Python 2.2之后,嵌套的作用域是可用的。 在这种情况下,你可以在任何级别的函数嵌套上绑定x,Python将拾取最内层的实例:

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

fun3从最接近的包含范围中看到实例x,这是与fun2关联的函数范围。 但是fun1和全局中定义的其他x个实例不受影响。

在nested_scopes之前 – 在2.1以前版本和2.1版本中,除非你使用from-future-import-fun1特别要求特性,fun2的作用域对fun3不可见,所以S.Lott的答案成立了,你将得到全局x :

 0 0