目录
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