Short description of the scoping rules?
python的作用域规则到底是什么?
如果我有一些代码:
1 2 3 4 5 6 7 8 | code1 class Foo: code2 def spam..... code3 for code4..: code5 x() |
在执行过程中,当函数
在某个地方必须有一个简单的引用或算法。对于中级的Python程序员来说,这是一个令人困惑的世界。
实际上,关于Python范围解析的一个简明规则,摘自《学习Python》,第3版。(这些规则特定于变量名,而不是属性。如果没有句号,则适用本规则)
莱布规则
l,局部名称—在函数(
e,封闭函数locals—任何和所有静态封闭函数(
g,global(module)-在模块文件的顶层分配的名称,或通过在文件中执行
B,内置(python)-内置名称模块中预先指定的名称:
所以,在
1 2 3 4 5 6 7 8 | code1 class Foo: code2 def spam..... code3 for code4..: code5 x() |
for循环没有自己的命名空间。按legb顺序,作用域是
L:本地,在
e:封闭函数,任何封闭函数(如果整个示例在另一个
G:全球。模块(
B:python中任何内置的
从本质上讲,Python中引入新范围的唯一东西就是函数定义。类有点特殊,因为直接在主体中定义的任何内容都放在类的命名空间中,但是它们不能从包含的方法(或嵌套类)中直接访问。
在您的示例中,只有3个范围将在其中搜索x:
垃圾邮件的范围-包含在code3和code5中定义的所有内容(以及code4,循环变量)
全局范围-包含代码1中定义的所有内容以及foo(以及其后的任何更改)
内置命名空间。有点特殊——它包含各种Python内置函数和类型,如len()和str()。一般来说,这不应该被任何用户代码修改,所以希望它包含标准函数,而不包含其他任何函数。
只有在图片中引入嵌套函数(或lambda)时,才会出现更多范围。不过,它们的表现将与您预期的差不多。嵌套函数可以访问本地范围内的所有内容,也可以访问封闭函数范围内的所有内容。如。
1 2 3 4 5 6 7 | def foo(): x=4 def bar(): print x # Accesses x from foo's scope bar() # Prints 4 x=5 bar() # Prints 5 |
限制:
可以访问除局部函数变量以外的作用域中的变量,但如果没有进一步的语法,则无法恢复到新参数。相反,赋值将创建一个新的局部变量,而不是影响父范围中的变量。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
为了在函数范围内实际修改全局变量的绑定,需要使用global关键字指定该变量是全局变量。如:
1 2 3 4 | global_var = 4 def change_global(): global global_var global_var = global_var + 1 |
目前,对于封闭函数作用域中的变量没有办法做到这一点,但是python 3引入了一个新的关键字"
关于Python3的时间,没有一个彻底的答案,所以我在这里做了一个答案。好的。
正如其他答案中提供的,有4个基本范围,即legb,用于本地、封闭、全局和内置。除此之外,还有一个特殊的范围,即类体,它不包含在类内定义的方法的封闭范围;类体内的任何赋值都会使从此处开始的变量被绑定到类体中。好的。
尤其是,除了
展示班级的特点好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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) |
因此,与在函数体中不同,您可以将变量重新分配到类体中的相同名称,以获得具有相同名称的类变量;进一步查找此名称可以解决改为类变量。好的。
对于许多新加入Python的人来说,最大的惊喜之一是
1 2 3 | >>> [ i for i in range(5) ] >>> i 4 |
这些理解可以作为一种巧妙的方法(如果你愿意的话,也可以是糟糕的)在python2中的lambda表达式中创建可修改的变量——lambda表达式确实创建了一个变量范围,就像
这种行为已经在python3中得到了修复——没有理解表达式或生成器泄漏变量。好的。
全局实际上是指模块的作用域;python主模块是
如果在当前作用域(类作用域除外)中曾经分配名称,则该名称将被视为属于该作用域,否则将被视为属于分配给变量的任何封闭作用域(可能尚未分配该名称,或根本没有分配该名称),或最终属于全局作用域。如果变量被视为局部变量,但尚未设置或已被删除,则读取变量值将导致
1 2 3 4 5 6 7 | 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() |
作用域可以声明它显式地想要用global关键字修改global(module scope)变量:好的。
1 2 3 4 5 6 7 8 | x = 5 def foobar(): global x print(x) # -> 5 x += 1 foobar() print(x) # -> 6 |
这也是可能的,即使它被隐藏在封闭范围内:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
在python2中,修改封闭范围中的值不是一个简单的方法;通常通过一个可变的值来模拟,例如长度为1的列表:好的。
1 2 3 4 5 6 7 8 9 10 11 | 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 |
但是在python3中,
1 2 3 4 5 6 7 8 9 | 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. |
任何不被视为当前作用域或任何封闭作用域局部的变量都是全局变量。在模块全局字典中查找全局名;如果找不到全局名,则从内置模块中查找全局名;模块名从python 2更改为python 3;在python 2中,它是
读取内置模块也很有用;假设您希望在文件的某些部分使用python3样式的打印函数,但文件的其他部分仍然使用
1 2 3 | import __builtin__ print3 = __builtin__.__dict__['print'] |
python 2.x的作用域规则已经在其他答案中进行了概述。我唯一要补充的是,在python 3.0中,还有一个非本地范围的概念(由"non local"关键字表示)。这允许您直接访问外部作用域,并打开了执行一些巧妙技巧的能力,包括词汇闭包(不需要涉及可变对象的丑陋黑客)。
编辑:这是关于这方面的更多信息的政治公众人物。
更完整的范围示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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() |
输出:
1 2 3 4 5 6 7 8 | 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 |
python使用三个可用的名称空间来解析变量。
At any time during execution, there
are at least three nested scopes whose
namespaces are directly accessible:
the innermost scope, which is searched
first, contains the local names; the
namespaces of any enclosing functions,
which are searched starting with the
nearest enclosing scope; the middle
scope, searched next, contains the
current module's global names; and the
outermost scope (searched last) is the
namespace containing built-in names.
有两个函数:
名称空间是由包、模块、类、对象构造和函数创建的。没有其他风格的名称空间。
在这种情况下,必须在本地名称空间或全局命名空间中解析对名为
在这种情况下,局部是方法函数
全球就是——好吧——全球。
规则是搜索方法函数(和嵌套函数定义)创建的嵌套局部空间,然后搜索全局。就是这样。
没有其他范围。
在类定义中,名称是类命名空间的一部分。例如,
对象(类的实例)具有实例变量。这些名称位于对象的命名空间中。它们必须由对象限定。(
在类方法中,您有局部变量和全局变量。你说
参见python scope rules、python scope、variable scope。
Where is x found?
未找到X,因为您尚未定义它。:-)如果放在代码1(全局)或代码3(本地)中,可以找到它。
代码2(类成员)对同一类的方法内的代码不可见?-?您通常会使用self访问它们。code4/code5(循环)与code3的作用域相同,因此如果在其中写入x,您将更改code3中定义的x实例,而不是生成新的x。
python的作用域是静态的,所以如果您将"spam"传递给另一个函数,spam仍然可以访问它来自的模块(在code1中定义)中的全局,以及任何其他包含作用域的函数(见下文)。代码2成员将再次通过self访问。
lambda与def没有区别。如果在函数内部使用lambda,则与定义嵌套函数相同。在python 2.2之后的版本中,可以使用嵌套范围。在这种情况下,可以在任何级别的函数嵌套中绑定x,而python将获取最内部的实例:
1 2 3 4 5 6 7 8 9 10 11 12 | x= 0 def fun1(): x= 1 def fun2(): x= 2 def fun3(): return x return fun3() return fun2() print fun1(), x 2 0 |
fun3从最近的包含范围(即与fun2关联的函数范围)中查看实例x。但在fun1和global中定义的其他x实例不受影响。
在嵌套的作用域之前?-?在python pre-2.1和2.1中,除非您特别要求使用将来导入的特性?-?fun1和fun2的作用域对fun3不可见,所以s.lott的答案是成立的,您将得到全局x:
1 | 0 0 |