day15:一文弄懂名称空间与作用域

1. 命名关键字参数(了解)

命名关键字参数:在定义函数时,*后定义的参数,如下所示,称之为命名关键字参数

特点:①命名关键字实参必须按照key=value的形式为其传值

def func(x,y,*,a,b): # 其中,a和b称之为命名关键字参数
    print(x,y)  # 1 2
    print(a,b)

func(1,2,b=222,a=111)  # 111 222

示例:命名关键字参数中,其参数顺序不讲究 ,不影响

def func(x,y,*,a=11111,b):
    print(x,y)   # 1 2
    print(a,b)

func(1,2,b=22222)  # 11111 22222

组合使用(了解)
形参混用的顺序:位置新参,默认形参,*args,命名关键字形参,**kwargs

def func(x,y=111,*args,z,**kwargs):  # 不会报语法错误
    print(x)
    print(y)
    print(args)
    print(z)
    print(kwargs)

实参混用的顺序:

def func(x,y,z,a,b,c):
    print(x)
    print(y)
    print(z)
    print(a)
    print(b)
    print(c)

# func(111,y=222,*[333,444],**{'b':555,'c':666})
#先打散 ==>func(111, y=222, 333, 444, b=555, c=666)  位置实参在默认实参前,报错


func(111,*[333,444],a=222,**{'b':555,'c':666})
# 先打散 ==> func(111,333,444,a=222,b=555,c=66)

func(111,*[333,444],**{'b':555,'c':666},a=222,)
# 先打散 ==> func(111,3333,4444,b=555,c=666,a=222)

2. 名称空间namespaces

在这里插入图片描述

作用:存放名字的地方,是对栈区的划分。有了名称空间之后,就可以在栈区中存放相同的名字,名称空间分为三种

2.1 内置名称空间

存放的名字:存放的python解释器内置的名字

存活周期:python解释器启动则产生,python解释器关闭则销毁

>>> print
<built-in function print>
>>> input
<built-in function input>

2.2 全局名称空间

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

'''文件执行过程中产生的名字都会存放于该名称空间中,如下名字'''

'''举例:'''
import sys #模块名sys

x=1 #变量名x

if x == 1:
    y=2 #变量名y

def foo(x): #函数名foo
    y=1
    def bar():
        pass

Class Bar: #类名Bar
    pass

3.3 局部名称空间

存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
存活周期:在调用函数时存活,函数调用完毕后则销毁

'''举例:'''
def foo(x):
    y=3  #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中

3.4 名称空间的加载顺序

内置名称空间>全局名称空间>局部名称空间

3.5 销毁顺序

局部名称空间>全局名空间>内置名称空间

3.6 名字的查找优先级

当前所在的位置向上一层一层(是向上一层空间找,不是顶头上面找)查找

内置名称空间
↑↑↑
全局名称空间
↑↑↑
局部名称空间

3.6.1 举例1:如果当前在局部名称空间

查找顺序:局部名称空间—>全局名称空间->内置名称空间

input=333

def func():
    input=444
    print(input)

func()  #  444 

3.6.2 举例2:如果当前在全局名称空间

input=333

# 如果当前在全局名称空间
def func():
    input=444
    print(input)

func()  #  444 注:当前在局部名称空间找,有input=444,所以print(input) 结果为444
print(input)  # 333 注:不在函数内,现在全局名称空间找,有input=333,结果为333

3.6.3 示范:函数嵌套的案例

注:下述案例中使用input作为变量,主要是为了让我们明白内置名称空间,理论上最好不要用内置函数名作为变量名

'''示范1: 局部名称空间没有,往全局名称空间找'''

def func():
    print(x)
x=111

func()  #  111

【重点】示范2:名称空间的“嵌套”关系是以定义截断为准的

''' 示范2:名称空间的"嵌套"关系是以函数定义阶段为准,“***与调用位置无关” '''
'''
解释:当func()函数被调用时,它不会创建新的变量x,因为它没有在内部定义x。因此,func()函数会查找其所在的作用域链来查找变量x。
在func()函数中,由于没有局部变量x,Python会查找更外层的作用域——即全局作用域。在全局作用域中,它找到了变量x,其值为111。
'''
x=111
def func():
   print(x)

def foo():
    x=222
    func()

foo()   # 111 
'''示范3.1:函数嵌套定义'''
'''
解读:函数f1是一个外部定义的函数,无输入参数,input=222在f1()函数的作用域内;
     f2是f1的内嵌函数,无输入参数,input=333在f2()的作用域内;
     调用f1()时没有没有调用到f2(),因为f1中只定义了f2(),而f2()中没有打印input变量的值,所以输出为空
'''
input=111
def f1():
	input=222
    def f2():
        input=333
        print(input)
        
f1()  # 无输出结果
'''示范3.1 f2内嵌到f1,调用f1时其实没调用到f2,所以f2中的只是起到了定义作用,
           在调用时啥也没干,故只输出f1中的打印'''

input=111
def f1():
    input = 222
    def f2():
        input=333
        print(input)

    print(input)

f1()   # 222
'''示范3.2:函数嵌套定义,在 f1 内部,f2() 被调用。由于 f2 在其自己的作用域
           内找不到名为 input 的变量,它会回退到 f1 的作用域,
           并找到那里的 input(值为 222)。
'''

input=111
def f1():
    input=222
    def f2():
        #input=333  
        print(input)
    f2()
    
f1()  # 222
'''示范3.3:函数嵌套定义,屏蔽f1()、f2()中的input,使用全局名称空间的值'''

input=111
def f1():
    # input=222
    def f2():
        # input=333
        print(input)
    f2()

f1() # 结果:111
'''示范3.4:函数嵌套定义,屏蔽f1()、f2()中的input,屏蔽全局名称空间的input,使用内置名称空间的值'''
# input=111
def f1():
    # input=222
    def f2():
        # input=333
        print(input)
    f2()

f1() # 结果:<built-in function input>
''' 示范4:函数定义时没语法错误,调用时存在语法错误,x 定义前被调用 '''
x=111
def func():
    print(x)
    x=222

func()  # UnboundLocalError: local variable 'x' referenced before assignment

3. 作用域

3.1 全局作用域与局部作用域

按照名字作用范围的不同可以将三个名称空间划分为两个区域:

全局作用域:位于全局名称空间、内建名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用);

局部作用域:位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)

3.2 作用域与名字查找的优先级

在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,没有找到,再去全局作用域查找:先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常

全局作用域:内置名称空间、全局名称空间
1、全局存活
2、全局有效:被所有函数共享

''' 案例1:全局作用域 '''

x=111   # 全局作用域的x

def foo():
    print(x,id(x))  # 在局部找x

def bar():
    print(x,id(x))  # 在局部找x

foo()  # 在局部找x,找不到,再在全局作用域找x
bar()  # 在局部找x,找不到,再在全局作用域找x

print(x,id(x)) # 在全局中找x

在这里插入图片描述
局部作用域: 局部名称空间的名字
1、临时存活
2、局部有效:函数内有效

'''案例2:局部作用域'''
x=1
def outer():
    x=2
    def inner(): # 函数名inner属于outer这一层作用域的名字
        x=3
        print('inner x:%s' %x)

    inner()
    print('outer x:%s' %x)

outer()

# inner x:3
# outer x:2

3.3 global关键字

在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字
3.3.1 当值为不可变类型时,使用global关键字

x=1
def foo():
    global x #声明x为全局名称空间的名字
    x=2
    print(x)
foo()     #结果为2
print(x) #结果为2

3.3.2 当实参的值为可变类型时,函数体内对该值的修改将直接反应到原值

num_list=[1,2,3]
def foo(nums):
    nums.append(5)

foo(num_list)
print(num_list) #结果为 [1,2,3,5]

3.3.3 nonlocal关键字

对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)

'''案例1:'''
def  f1():
    x=2
    def f2():
        nonlocal x
        x=3
    f2() #调用f2(),修改f1作用域中名字x的值
    print(x) #在f1作用域查看x

f1()  # 结果:3
'''案例2:'''
def  f1():
    x=1
    def f2():
        x=2
        def f3():
            nonlocal x
            x=3
            print(x) #在f3作用域查看x
        f3()  # 调用f3(),修改f2作用域中名字x的值
        print(x) #在f2作用域查看x
    f2()
    print(x)

f1()

在这里插入图片描述

4.作业:

下述所有代码画图以及分析代码执行流程
1、以定义阶段为准,先画出名称空间的嵌套关系图
2、然后找到调用函数的位置,写出函数调用时代码的执行过程,涉及到名字的查找时,参照1中画好
的嵌套图,标明查找顺序,一层一层直到找到位置

题目一:

input=333
def func():
    input=444
    print(f'func内部的:{input}')  # 结果:444
func()
print(f'全局的:{input}')   # 结果:333

解答:print(f’全局的:{input}‘) 是顶头代码,从全局名称空间找,看下图为定义阶段的嵌套图关系
①func()含义:先找func的名子,再调用。func()顶头写,说明在全局名称空间找func的名字。全局名称空间func指向局部名称空间的函数func,调用函数,有定义的input=444,有找名字input的动作 print(f’func内部的:{input}’),先在函数func局部名称空间找,发现有input=444,故 print(f’func内部的:{input}‘)的值为444。
②顶头的print(f’全局的:{input}’) 直接在全局找input,故值为333

引申:如果注释掉input=444,结果是怎样???
在这里插入图片描述

题目二:

def func():
    print(x)
x=111

func()

解答:调用func时有运行print(x),先从当前局部名称空间找x,没找到,然后在全局中找到了x=111,故结果为:111
在这里插入图片描述

题目三

x=1
def func():
   print(x)
   
def foo():
    x=222
    func()

foo() # 结果:1

解答:
①一行行解释,下图为定义截断所画出的便于理解的名称空间图;

②foo()的含义:先找foo的名子,再调用。foo()顶头写,说明在全局名称空间找foo的名字。foo名字在全局名称空间,指向的是局部名称空间函数foo,执行了x=222,函数内有func();

③func()的含义:先在func的名字,再调用。在foo的局部作用域找不到func名字,再去全局作用域找,找到了有func的内存地址,名字func指向局部名称空间的函数func,里面执行了print(x)表明在找名字x,先在函数func作用域找,没找到,再去全局找,发现全局有x=1,故print(x) 结果为1
在这里插入图片描述

题目四:

input=111
def f1():
    def f2():
        # input=333
        print(input)
    input=222

    f2()

f1()  # 结果:222

在这里插入图片描述

题目五:

x=111
def func():
    print(x) #
    x=222

func()  # 结果:报错,UnboundLocalError: local variable 'x' referenced before assignment

解释:函数定义截断,上述均没异常,但是在调用函数func时才会触发异常,因为赋值x之前使用了x
在这里插入图片描述

题目六:

x=111

def foo():
    print(x,)

def bar():
    print(x)

foo()  # 结果:111
bar()  # 结果:111

在这里插入图片描述

题目七:

x=1
def func2():
    func1()
x=2
def func1():
    print(x)
x=3

func2() # 结果:3

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值