2.4.2 名称空间

本节目标:

  • 了解LEGB名称空间。

  • 能指出之前写过的程序中的变量都属于哪个名称空间。

  • 了解闭包。

当我们使用turtle库中的类时,我们用turtle.Turtle。为什么要把库名加上? 原因很容易想到,因为别的库中也可能有Turtle

y = 3
def func():
    y = 1
    print("函数func:", y) # 输出:1
func()
print("函数外:", y) # 输出:3

运行以上代码我们发现,在函数里面对y的值进行改变似乎没有影响到函数外y的值。

Learning By Reading 难度:★★ 重要性:★★★★★

  • 阅读材料,了解Python的命名空间

  • 材料中有一个链接很好地解释了“闭包”的概念。

    里面提到的“装饰器”将在下一节介绍,可以先跳过与“装饰器”有关的部分。

另一个例子,

def polygon_factory(n):
    # 返回一个正n边形函数polygon
    def polygon(turtle, side):
        return turtle.polygon(n, side) # 绑定了闭包polygon_factory中的名称n和局部名称空间中的turtle和side
        # 翻译成人话即这里的n用的是polygon_factory的参数n,而turtle和side是局部变量
    return polygon

pentagon = polygon_factory(5) # 五边形函数
octagon = polygon_factory(8) # 八边形函数
my_turtle = MyTurtle()
pentagon(my_turtle, 50) # 边长为50的五边形
octagon(my_turtle, 50) # 边长为50的八边形

每个名称都有自己的“作用域”(scope),即这个名称可以在哪个范围内被绑定。 比如上面的turtleside,其作用域是polygon这个函数。 出了这个函数,Python就不认turtleside这个名字了。 而polygonn,其作用域是polygon_factory这个函数,也包括在polygon_factory里面定义的polygon函数。

def polygon_factory(n):
    def polygon(turtle, side):
        print(n, polygon) # 可以
        print(turtle, side) # 可以
        return turtle.polygon(n, side)
    print(n, polygon) # 可以
    print(turtle) # 报错
    print(side) # 报错
    return polygon

而靠里面的名称会覆盖掉靠外面的名称,本节第一段代码就是例子。

另外一点需要注意的是,函数体中的名称,其作用域可以扩展到函数中定义的函数里。 就像上面的n可以在polygon中使用。

但类体中的名称不会扩展到类方法和列表解析中。

master = "Your Name"

class MyTurtle(turtle.Turtle):

    yet_another_master = "Your BF/GF's Name"
    other_masters = [yet_another_master + str(i) for i in range(42)] # 不可以
    print(master) # 可以,输出全局变量master
    print(yet_another_master) # 可以

    def polygon(self):
        print(master) # 可以
        print(yet_another_master) # 报错

class MyOtherTurtle(object):

    master = "Your Nickname"
    other_masters = [master + str(i) for i in range(42)] # 可以,但这里的master是全局变量master,即"Your Name",而不是"Your Nickname"

    def polygon(self):
        print(master) # 可以,仍然是"Your Name",而不是"Your Nickname"

x的作用域延伸到了some_method里,但y的作用域不能。

另一个区别是

master = "Your Name"

def my_turtle():
    yet_another_master = master # 报错:UnboundLocalError,Python认为x这时候还没有赋值
    master = 42

class MyTurtle(turtle.Turtle):
    yet_another_master = master # 可以,虽然这个时候局部名称master还没有绑定,但因为是在类体中,所以Python会用全局变量master
    master = "Your Nickname"

print(MyTurtle.master) # 输出:"Your Nickname"
print(MyTurtle.yet_another_master) # 输出:"Your Name"
print(master) # 输出:"Your Name"

在函数中,只要用到了变量x,那么,函数体中的所有x都会被绑定到局部名称空间的x, 所以y = x的时候会报错说“变量x在赋值前引用”。

而类体中,遇到这种情况时,Python会自己去全局名称空间里面找x

master = "Your Name"

def get_my_turtle():
    master = "Your Nickname"

    class MyTurtle(turtle.Turtle):
        yet_another_master = master # 由于局部名称空间中存在master,所以会有UnboundLocalError,但这是类体,所以Python会去全局名称空间找x,找到全局空间中的master是"Your Name"
        master = "Your GF/BF's Name"

    return MyTurtle

MyTurtle = get_my_turtle()
print(MyTurtle.master, MyTurtle.yet_another_master) # 输出:"Your GF/BF's Name","Your Name"
# 注意两个MyTurtle的区别。既然我们已经讲了好多好多名称空间呀作用域呀之类的例子了,不许再分不清了~!(..•˘_˘•..)

Last updated