资讯专栏INFORMATION COLUMN

python基础教程:作用域和命名空间

wangzy2019 / 1802人阅读

摘要:正如我们前面举的张三的例子那样,不同命名空间中的名称之间没有任何关系。作用域作用域,是一个命名空间可直接发放完的代码的文本区域。删除也是如此,语句会从局部命名空间的引用中移除对的绑定。

命名空间和作用域的概念我们之前也提到过,比如内置函数globals(),函数中变量的作用域,模块使用的import等等。这些可能让我们对这两个概念有了大致的理解。本节再详细探讨一下。

Python命名空间

命名空间,就是一个从名称到对象的映射关系。

对于这个概念的理解,我们打个比方:河西村有个人(对象)叫张三(名称),河东村有个人(对象)也叫张三(名称),俩人虽然都叫张三(名称),但是他们俩不是同一个人(对象),因为他们属于不同的村(命名空间)。有一天,河西村的张三名声大了,传播到镇上了(名称被import到“镇”这个空间),镇上的人讲起“张三”时,就是说的河西村的,要是说河东村的张三,就要特别说“河东村的张三”(河东村.张三)。这就是命名空间的意思——映射了名称到对象的名称范围。

目前,大部分的命名空间都是由Python的字典实现的,通常我们不会去关注它们,处理要面对性能问题时,并且这种实现可能在将来改变。所以说,我们不需要深究命名空间的内部实现,但要搞明白它的使用。

下面是几个命名空间的例子:

内置函数的集合(包含print()等内置函数和内置异常等);

模块中的全局名称;

函数调用中的本地名称。

另外,从某种含义上说,对象的属性集合也是一种命名空间的形式。正如我们前面举的张三的例子那样,不同命名空间中的名称之间没有任何关系。比如,两个不同模块都可以定义函数max()而不会产生混淆,模块的用户要调用某个max()函数就要在其前面加上模块名称。(详见import的使用)

Python属性

我们把任何跟在一个点号之后的名称都称为属性。例如,在表达式a.name中,real是对象a的一个属性。同样对模块中函数的引用也是属性引用,在表达式modname.funcname中,modname是一个模块对象,而funcname是它的一个属性。

属性可以是只读的也可以是可写的。如果是可写的,那么我们就可以对属性进行赋值,比如,modname.name = "猿人学Python"。可写的属性同样可以用del语句删除,比如del modname.name将把modname对象的name属性移除。

不同时刻创建的命名空间有不同的生存期:

包含内置名称的命名空间是在Python解释器启动时创建的,永远不会被删除(除非退出解释器);

模块的全局命名空间在模块定义被读入(import)时创建,通常,模块命名空间也会持续到解释器退出;

从脚本文件(.py.pyc)读取或交互式(解释器shell)读取而被解释器的顶层调用执行的语句,被认为是__main__模块调用的一部分,它们有自己的全局命名空间;

函数的本地命名空间创建于该函数被调用的时刻,并且在函数返回或抛出一个不在函数内部处理的异常时被删除。递归函数的每次递归调用都会创建它自己的本地命名空间;

内置名称实际上也存在于一个模块中,它叫做builtins

Python作用域

作用域,是一个命名空间可直接发放完的Python代码的文本区域。这里的“可直接访问”的意思是,对名称的不加点号(非限定性)引用会尝试在命名空间中查找该名称。

尽管作用域是静态确定的,但它们是动态使用的。在执行期间的任何时刻,至少有三个嵌套的作用域,它们的命名空间可以直接访问:

最内部作用域:最先搜索该作用域,包含局部名称

封闭函数作用域:从最近的封闭作用域开始搜索,包含非局部名称,也包括非全局名称

倒数第二个作用域:包含当前模块的全局名称

最外面的作用域:最后搜索,是包含内置名称的命名空间

如果一个名称被声明为全局变量,则所有引用和赋值将直接指向包含该模块的全局名称的中间作用域。 要重新绑定在最内层作用域以外找到的变量,可以使用nonlocal语句声明为非本地变量。 如果没有被声明为非本地变量,这些变量将是只读的(尝试写入这样的变量只会在最内层作用域中创建一个新的局部变量,而同名的外部变量保持不变)。

很重要的一点:作用域是按文本方式确定的,模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用。另一方面,实际的名称搜索是在运行时动态完成的。

Python 的一个特殊之处在于: 如果不存在生效的global语句,对名称的赋值总是进入最内层作用域。 赋值不会复制数据,它们只是将名称绑定到对象。删除也是如此,语句del x会从局部命名空间的引用中移除对x的绑定。事实上,所有引入新名称的操作都使用局部作用域,特别是import语句和函数定义会在局部作用域中绑定模块或函数名称。

global语句可被用来表明特定变量生存于全局作用域并且应当在其中被重新绑定;nonlocal语句表明特定变量生存于外层作用域中并且应当在其中被重新绑定。

下面我们来看一个作用域和命名空间的例子,它演示流量如何引用不同作用域和命名空间以及globalnonlocal如何影响变量绑定:

def scope_demo():
    def do_local():
        name = "local name"

    def do_nonlocal():
        nonlocal name
        name = "nonlocal name"

    def do_global():
        global name
        name = "global name"

    name = "demo name"
    do_local()
    print("After local assignment:", name)
    do_nonlocal()
    print("After nonlocal assignment:", name)
    do_global()
    print("After global assignment:", name)

scope_demo()
print("In global scope:", name)

思考一下,上面的代码会输出怎样的结果?如果你对上面的讲解理解透了,你的思考结果应该是这样的:

After local assignment: demo name
After nonlocal assignment: nonlocal name
After global assignment: nonlocal name
In global scope: global name

这里要说明的是,do_global()函数修改了全局变量name,并没有对scope_demo()函数的局部变量name做修改,所以打印了After global assignment: nonlocal name

局部赋值(默认情况)不会改变scope_demoname的绑定;nonlocal赋值会改变函数scope_demoname的绑定,而global赋值会改变模块层级的绑定(不是scope_demo内部的name,而是其之外的全局作用域下的name)。

命令空间和作用域总结:

命名空间定义了一个名称的范围,作用域指定了能看到命名空间的文本区域(代码)。代码执行时,名称搜索的顺序和范围如下:

最内部作用域:最先搜索该作用域,包含局部名称

封闭函数作用域:从最近的封闭作用域开始搜索,包含非局部名称,也包括非全局名称

倒数第二个作用域:包含当前模块的全局名称

最外面的作用域:最后搜索,是包含内置名称的命名空间

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43845.html

相关文章

  • Python基础教程

    摘要:函数内的变量被称为局部变量,这是与全局变量相反的概念。有一些进行函数式编程的机制。继承以通用的类为基础建立专门的类对象。 6.4.5 参数收集的逆过程 假设有如下函数: def add(x,y): return x+y 比如说有个包含由两个相加的数字组成的元组: params = (1,2) 使用*运算符对参数进行分配,不过是在调用而不是在定义时使用: >>> add(*params)...

    daydream 评论0 收藏0
  • python基础教程:类

    摘要:类对象被创建时存在于类命名空间内的所有名称都是有效的属性名称。类的实例化,是使用函数表示法,可以把类对象看做是会返回一个新的类实例的函数。这就是可变对象作为类变量时的特性。类变量是所有类的实例共享的属性和方法,实例变量是每个实例独有的数据。 Python是面向对象的高级编程语言,在Python里面一切都是对象:数字、字符串、元组、列表、字典、集合等内置数据类型,以及函数、方法、类、模块...

    fish 评论0 收藏0
  • 一道题看Python的LEGB规则

    摘要:例题核心编程第二版变量作用域和命名空间一节有以下一道题目请问输出结果是什么要想解这道题,必须先了解中的一些概念的变量名解析机制有时称为。 例题 《核心编程(第二版)》变量作用域和命名空间一节有以下一道题目 # coding=utf-8 #!/usr/bin/env python def proc1(): j,k = 3,4 print j == %d and k ==...

    iamyoung001 评论0 收藏0
  • Python学习--最完整的基础知识大全

    摘要:迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器有两个基本的方法和。调用一个生成器函数,返回的是一个迭代器对象。生成器函数生成器函数返回一个迭代器函数函数传入不定长参数加了星号的变量名会存放所有未命名的变量参数。 Python学习--最完整的基础知识大全 关于python的基础知识学习,网上有很多资料,今天我就把我收藏的整理一下分享给大家! 菜鸟教程pytho...

    王笑朝 评论0 收藏0
  • Python基础教程》第六章--读书笔记

    摘要:第六章抽象本章会介绍如何将语句组织成函数。关键字参数和默认值目前为止,我们使用的参数都是位置参数,因为它们的位置很重要,事实上比它们的名字更重要。参数前的星号将所有值放置在同一个元祖中。函数内的变量被称为局部变量。 第六章:抽象 本章会介绍如何将语句组织成函数。还会详细介绍参数(parameter)和作用域(scope)的概念,以及递归的概念及其在程序中的用途。 懒惰即美德 斐波那契数...

    AnthonyHan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<