作用域(scope)

作用域是在一个定义范围内引用变量(名称)的能力。作用域 定义了一个代码块中变量(名称)的可见性。 如果代码块中定义了一个局部变量,则其作用域包含该代码块。 如果定义发生于函数代码块中,则其作用域会扩展到该函数所包含的任何代码块,除非有某个被包含代码块引入了对该名称的不同绑定。

分类

只有函数、类、模块会产生作用域,if,whilr,for代码块没有作用域。作用域按照变量的定义位置可以划分为4类:
Local(函数内部)局部作用域
Enclosing(嵌套函数的外层函数内部)嵌套作用域(闭包)
Global(模块全局)全局作用域
Built-in(内建)内建作用域

例如:

 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
30
31
32
33
34
35
36
# -*- coding: UTF-8 -*-
# filename: test.py

# 变量对象s拥有全局作用域
s = "Hello"

# 变量对象a虽然处于if代码块中,但没有独自的作用域。变量对象a拥有全局作用域。
if True:
    a = 100

# 变量对象c拥有类A的作用域
class A:
    c = "class"

# 函数对象hello拥有全局作用域
def hello(name):
    # 变量对象h拥有局部作用域
    h = s + name
    # 函数对象print是随Python解释器启动而来的,拥有内建作用域。
    print(h)

# 函数对象outer拥有全局作用域
def outer():
    args = 200
    # 函数对象inner拥有嵌套作用域,函数内部能访问外层函数outer的变量args
    def inner():
        print(args)
    return inner

print(A.c)     # 输出 class

hello(" Tom")  # 输出 Hello Tom
print(a)       # 输出 100

p = outer()
p()     # 输出 200

命名空间与作用域的关系

命名空间定义了在某个作用域内变量名和绑定值之间的对应关系,命名空间是键值对的集合,变量名与值是一一对应关系。作用域定义了命名空间中的变量能够在多大范围内起作用。

命名空间在python解释器中是以字典的形式存在的,是以一种可以看得见摸得着的实体存在的。作用域是python解释器定义的一种规则,该规则确定了运行时变量查找的顺序,是一种形而上的虚的规定。

变量查找顺序

python解释器动态执行过程中,对遇到的变量进行解释时,是按照一条固定的作用域链查找解释的,又称为LEGB法则。 其中L代表Local 局部作用域,E代表Enclosing 嵌套作用域,G代表Global 全局作用域,B代表Built-in 内建作用域。 python解释器查找变量时,会按照顺序依次查找局部作用域,嵌套作用域,全局作用域,内建作用域,在任意一个作用域中找到变量则停止查找,所有作用域查找完成没有找到对应的变量,则抛出 NameError: name ‘xxxx’ is not defined的异常。

在局部作用域中,可以看到局部作用域、嵌套作用域、全局作用域、内建作用域中所有定义的变量。

在全局作用域中,可以看到全局作用域、内建作用域中的所有定义的变量,无法看到局部作用域中的变量。如果Python在这些名字空间找不到x,它将放弃查找并引发一个NameError异常,如,NameError: name ‘x’ is not defined。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# -*- coding: UTF-8 -*-
# filename: test.py

#全局命名空间的变量对象s
s = "Hello"

#全局命名空间的函数对象hello
def hello(name):
    #局部命名空间的变量对象h
    h = s + name
    #函数对象print是随Python解释器启动而来的,属于内置命名空间。
    print(h)

hello(" Tom")  #输出 Hello Tom
print(h)       #抛出异常。NameError: name 'h' is not defined

执行以上程序会输出如下结果:

1
2
3
4
5
Hello Tom
Traceback (most recent call last):
  File "main.py", line 15, in <module>
    print(h)  
NameError: name 'h' is not defined

调用hello(” Tom”)时,需要打印变量h的值,h是在函数内部定义的局部变量,所以在hello函数的局部命名空间就找到了h。h的值是由变量s和变量name组成的,需要查找变量s和name。name是函数的形参,属于局部命名空间,调用时就传过来了它的值” Tom”。在hello局部命名空间是找不到变量s的值,那么就在全局变量空间test.py中查找,找到s的值是”Hello”,这样就打印出了hello的结果Hello Tom。 调用print(h)时,所有的空间内都找不到变量h的值,所以抛出异常NameError: name ‘h’ is not defined。

比较以下例子有啥不同?

1
2
3
4
5
6
7
name = "lzl"
def f1():
    print(name)
def f2():
    name = "eric"
    f1()
f2() #输出lzl
1
2
3
4
5
6
7
8
name = "lzl"
def f1():
    print(name)
def f2():
    name = "eric"
    return f1
ret = f2()
ret() #输出lzl
1
2
3
4
5
6
7
8
9
name = "lzl"
def f2():
    name = "eric"
    def f1():
        print(name)
    return f1

ret = f2()
ret() #eric

转载请注明本网址。