06-03 名称空间与作用域

一、名称空间(namespaces)

  • 提示:以下所有的定义都是基于脚本式运行程序是的定义

  • 拓展:查看python的文档注释 import this

"""
1、什么是名称空间?
	名称:定义的名字 空间:存放名字的地方
	举例变量名与内存地址绑定关系引入名称空间的概念:
		我们之前所知在python中名字与内存地址绑定关系的地方是在栈区,那我们的名称空间就是把栈区中的名字与内存地址的绑定关系做了归类。分别是内置名称空间、全局名称空间、局部名称空间。
2、栈区和名称空间哪个是真实存在的?
	名称空间只是虚拟的概念。栈区才是真正存在的。
3、名称空间有包含关系吗?
	名称空间之间本质是没有包含关系的,是互相独立的。

4、为什么要有名称空间?
	有了名称空间之后,就可以在栈区中存放相同的名字,让所有的名称不冲突。
查找名称空间的优先级:
	
5、三种名称空间的个数:
	内置、全局1只有一个个。局部可以多个。
"""

1、内建名称空间(也叫内置名称空间)

"""
存放的名字:存放的是python解释器内置的名字
存活周期:python解释器启动则产生,python解释器关闭则销毁。
"""
# 示例:
>>> print
<built-in function print>
>>> input
<built-in function input>

2、全局名称空间

"""
存放的名字:不是函数内定义、也不是内置的,剩下的就是全局名称空间的名字。
存活周期:python文件执行则产生,python文件运行完毕后生效。
"""

# 全局名称空间示例
# 1、导入模块也是os是全局名称空间中的名字
import os

# 2、x, y,z也是os是全局名称空间中的名字
x = 10
if 13 > 3:
    y = 20
    if 3 == 3:
        z = 30
# 3、注意:func是全局名称空间中的名字,但是a,b不是,它们属于局部名称空间
def func():  
    a = 111
    b = 222
    
# 4、Foo是全局名称空间中的名字    
class Foo: 
    pass

3、局部名称空间

"""
存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
存活周期: 在调用函数时存活,函数调用完毕后销毁
注意:函数只要调用一次就会产生一个局部名称空间,函数体内的名字就会存放在这个属于它们的独立的局部名称空间中。
"""
def func(a, b):
    pass

# 下面👇调用了4次func,虽然使用pass语句,但是在调用函数时,也会产生局部名称空间,且它们每调用一次,都会产生一个新的局部名称空间。
func(10, 1)
func(10, 1)
func(10, 1)
func(10, 1)

4、三种名称空间的加载顺序

"""
1、三种名称空间的加载顺序:
	内置名称空间 > 全局名称空间 > 局部名称空间
2、三种名称空间中一定要有的名称空间:
	内置名称空间、全局名称空间
3、三种名称空间的销毁顺序(了解):局部名称空间 > 全局名称空间 > 内置名称空间
4、三种名称空间的名字的查找优先级:在当前所在的位置,一层层向上查找
	如果当前在局部名称空间:
		局部名称空间 --> 全局名称空间 --> 内置名称空间
"""
# 一、三种名称空间的名字的查找优先级示例
# 1、如果当前在局部名称空间:局部名称空间 --> 全局名称空间 --> 内置名称空间、
input = 100
def func():
    input = 10
    print(input)  # 10
func()
    
# 2、如果当前在全局名称空间:全局名称空间 --> 内置名称空间
# 示范一:
input = 111
def func():
    input = 222
func()
print(input)  # 111

# 示范二:
x = 222
def func():
    print(x) # 111
x = 111  # 定义阶段print语句还没有执行,也就是说func中的名称空间还没有产生。当运行到这条代码,x与之前值222的内存地址的绑定关系解除。绑定了新的值111的内存地址。所以当调用func()时,func中的局部名称空间才产生,这个时候print在自己的名称空间中没有找到,跑到全局找到了x=111,所以上面返回111
func()

# 示范三(难点):名称空间的"嵌套"关系是以函数定义时为准的,与调用位置无关。
x = 1
def func():
    print(x)  # 1
    
def foo():
    x = 222
    func()
foo()    

# 示范四(难点):函数的嵌套定义
input = 111
def f1():
    def f2():
        print(input) # 222
	input = 222
    f2()
f1()


# 示范五(难点):逻辑错误
x = 111
def func():
    print(x)  # 报错(UnboundLocalError: local variable 'x' referenced before assignment)。
    x = 222
func()

二、作用域

  • 只是将对名称空间的划分

1、全局作用域与局部作用域

"""
1、什么是作用域?
	作用域就是更具名称空间的范围和特点的不同进一步做了归类。内置名称空间和全局名称空间属于全局作用域,局部名称空间属于局部作用域。
	
2、全局作用域分为:全局名称空间、内置名称空间
	特点:
		全局存活
		全局有效:被所有函数共享
3、局部作用域分为:局部名称空间的名字
	特点:
		临时存活
		局部有效:函数内有效 12.12   12.14
		
4、LEGB 
    L —— Local(function);  函数内的名字空间
    E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)
    G —— Global(module);   函数定义所在模块(文件)的名字空间
    B —— Builtin(Python);  Python内置模块的名字空间
"""
# 验证:全局存活、全局有效:被所有函数共享
x = 1
def foo():
    print(x, id(x), id(print))
def func():
    print(x, id(x), id(print))
foo()  # 1 140731452606112 2329486822992
func() # 1 140731452606112 2329486822992

# 验证:临时存活、局部有效:函数内有效(函数内都能访问到x)
x = 1
def foo(x):
    def f1(x):
        def f2(x):
            print(x)  # 1
        print(x)  # 1
        f2(x)
    print(x)  # 1
    f1(x)
foo(x)

# 拓展:LEGB
# B:builtin
# G: Global
def f1():
    # E: Enclosing function locals
    def f2():
        # E: Enclosing function locals
        def f3():
            # L: Local

2、global与nonlocal

"""
使用方式
	global x:声明这个x是全局的名字x。
"""
# global用处:如果想在局部修改全局的名字对于的值,且对应得一定要是不可变类的值,这个时候就使用global。(注意:global作用就是争对局部中修改全局中不可变类型的值)
# 示范一:在局部中想修改全局的x=200,但是并不起作用
x = 10
def func():
    x = 200
    print(x)
func()
print(x)

# 解决方法:global
x = 10
def func():
    global x
    x = 200
    print(x, id(x))  # 200 140731452612480
func()
print(x, id(x))  # 200 140731452612480

# 如果在局部中修改可变类型,直接修改便是,并不需要global。
li = []
def func():
    li.append(333)
    print(li, id(li))  # [333] 2025694165824
func()
print(li, id(li))  # [333] 2025694165824

"""
使用方式:
	nonlocal(了解):大前提:只能是在局部名称空间中查找。查找规则:先从外部嵌套得名称空间第一层找,没找到再从你外部的外部的嵌套函数的名称空间中找。如果这些嵌套的函数中都没找到,则报错。
"""
# 运用场景:修改函数外层函数包含的名字对应的值(注意:也是争对不可变类型)
# 1、不跨层的修改
def f1():
    x = 20
    def f2():
        nonlocal x
        x = 200
        print(x)  # 200
    f2()
    print(x)  # 200
f1()

# 2、跨层的修改
def f1():
    x = 20
    def f2():
        def f3():
            nonlocal x
            x = 200
            print(x)  # 200
        f3()
    f2()
    print(x)  # 200
f1()

# 3、注意:只会修改离自己最近的一层
def f1():
    x = 20
    def f2():
        x = 100
        def f3():
            nonlocal x
            x = 200
            print(x)  # 200
        f3()
        print(x)  # 200
    f2()
    print(x)  # 20
f1()

# 4、如果外层嵌套函数没有值,并不会去全局作用域。
x = 20
def f1():
    def f2():
        nonlocal x
        x = 200
        print(x)  # 报错(SyntaxError: no binding for nonlocal 'x' found)
    f2()
f1()
print(x)

# 5、如果是可变类型,直接修改便是,nonlocal也之争对不可变类型。
li = []
def f1():
    def f2():
        li.append(333)
        print(li)  # [333]
    f2()
f1()
print(li)  # [333]
posted @ 2020-03-19 19:27  给你加马桶唱疏通  阅读(216)  评论(0编辑  收藏  举报