Python 作用域和命名空间

坚持是一种品质 2019-12-02

Python Scopes and Namespaces

namespace is a mapping from names to objects. 

命名空间是一个从名字到对象的映射(指向,明确的路径)。

Most namespaces are currently implemented as Python dictionaries。

大部分都是由字典dict(其他语言也称为hash)实现。

例子:

the set of built-in names 。放置内置函数的集合。模块中的全局名称;函数调用中的局部名称。 从某种意义上说,对象的属性集合也是一种命名空间的形式。

namespaces is that there is absolutely no relation between names in different namespaces.

不同命名空间的名字之间没有关系,即相同的名字,在不同的命名空间是没有任何关系的。

生存周期lifetimes。

Namespaces are created at different moments and have different lifetimes.

例如:

模块的全局命名空间在模块定义被读入时创建,并持续到解释器退出。内置名称实际上也存在于一个模块builtins中。

函数的local namespace在函数被调用时创建,在函数返回或抛出一个不在函数内处理的?时被删除。

作用域

scope is a textual region of a Python program where a namespace is directly accessible.

作用域是一个代码区域,是一个命名空间可以直接引用的区域。

我的理解:

调用函数形成的函数内部的命名空间(映射/指针/路径/hash),在函数内部可以使用函数代码外定义的名字,由此得到名字对应的对象。那么函数代码外部的代码区域,就是一个可被这个函数直接使用的作用域。

??函数的外部作用域是在函数定义时,对应的位置,而不是调用时对应的位置。

??:参考了这篇文章:https://www.linuxidc.com/Linux/2018-12/155918.htm

 

命名空间的查找顺序:

 Local首先搜索,包含局部名字的最内层(innermost)作用域,如函数/方法/类的内部局部作用域;

 Enclosing根据嵌套层次从内到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封闭函数的作用域。如两个嵌套的函数,内层函数的作用域是局部作用域,外层函数作用域就是内层函数的 Enclosing作用域;

 Global倒数第二次被搜索,包含当前模块全局名字的作用域;

 Built-in最后被搜索,包含内建名字的最外层作用域。

Python按照以上LEGB的顺序依次在四个作用域搜索名字,没有搜索到时,Python抛出NameError异常。

用一个类比来理解命名空间与作用域:

  四种作用域相当于我们生活中的国家(Built-in)、省(Global)、市(Enclosing)、县(Local),命名空间相当于公务员花名册,记录着哪个职位是哪个人。国家级公务员服务于全国
民众(全国老百姓都可以喊他办事),省级公务员只服务于本身民众(国家层面的人或者其他省的人我不管),市(Enclosing)、县(Local)也是一个道理。当我们要找某一类领导(例如想找
个警察帮我打架)时(要访问某个名称),如果我是在县(Local)里头,优先在县里的领导花名册中找(优先在自己作用域的命名空间中找),县里花名册中没警察没有就去市里的花名册找(往
上一层作用域命名空间找),知道找到国家级都还没找到,那就会报错。如果省级民众想找个警察帮忙大家,不会找市里或者县里的,只会找自己省里的(其它省都不行),或者找国家级的。国家、
省、市、县肯定一直都在那里,可不会移动(作用域是静态的);领导可以换届,任期移到就换人(命名空间是动态的,每次调用函数都会新的命名空间,函数执行

glocal与nonlocal的区别

第一,两者的功能不同。

global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误。

def f1():
    # i = 1
    def f2():
        nonlocal i
        print(i)
        i = 2
    f2()
    print(i)

f1()
# SyntaxError: no binding for nonlocal ‘i‘ found

第二,两者使用的范围不同。

  • global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用。
  • nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误。
i = 0
def f1():
    i = 1
    def f2():
        global i     #重新绑定
        print("global", i)
        i = 2
    f2()
    print("local",i)

f1()
# 输出:
# global 0
# local 1

几个例子:

i = 1

def f1():
    print(i)

def f2():
    i = 2
    f1()

f2()

print(i)

#输出
#1
#1

解释:这个例子,说明了函数的外部作用域,是函数定义时的外部代码。

if True:

  i = 1

print(i) # 可以正常输出i的值1,不会报错

解释:这说明了if语句不产生新的作用域, 只有函数/类才会产生新作用域。

for i in range(10):

  pass

print(i) #输出结果是9,而不是NameError

解释: 无关作用域,i被声明,并被赋值。

def test():

    print(i)

  i= 2

i = 0

test()

解释:会报告错误,UnboundLocalError: local variable ‘i‘ referenced before assignment。i已经被占用了。所以不能再被重新声明。

class A(object):

    a = 2

    def fun(self):

        print(a)

new_class = A()

new_class.fun()
#NameError: name ‘a‘ is not defined

解释:还不理解。fun函数的外部作用领中,没有a。

相关推荐