函数参数(默认参数/关键字参数/位置参数/可变长参数)名称空间及作用域
今日内容概要
-
函数参数
- 函数参数:位置参数
- 函数参数:默认参数/关键字参数
- 函数参数:可变长参数
- 可变长形参
- 可变长实参
- 函数参数:命名关键字参数
-
名称空间理论及作用域
- 名字查找顺序
- 案例
-
练习题及答案
1.函数参数
位置参数:
位置参数
# 位置形参:函数定义阶段括号内从左到右依次填写的变量名,只要使用这种方法定义形参,就必须传值
例:
def subd(a, b, c):
pass
# 位置实参:函数调用阶段括号内从左到右依次填写的数据值(或是有数据值绑定的变量名)
def subd(1,2,3)
位置实参是在调用阶段获取参数并且临时赋值给位置形参
我们接下来在用几个例子解释一下它的实际情况:
def subd(a,b,c):
print(a,b,c)
subd(1,2,3) # 1,2,3依次赋值给a,b,c 调用函数结果为打印 1 2 3
subd(1,2) # 会直接报错,因为位置参数c没有填写到实参中,没有获取到数据值就会报错
# subd() missing 1 required positional argument: 'c'
subd(1,2,3,4) # 会直接报错,因为位置形参定义一共只有三个,不能多写
subd(a = 1, b =3, c = 5) # 1 3 5 变量名+赋值符号+数据值:关键字传参(指定对象传参)
subd(b = 1, a = 3,c = 5) # 3 1 5 使用关键字传参就可以不按照位置参数的要求来对应实参内容
# 可以修改实参内的参数放置位置,但是同样也不能多也不可以少!
subd(1,2,b = 1) # 报错 ,因为按照位置参数 1,2已经赋值给a,b了 ,所以应该传输c的参数
subd(1,2,c = 1) # 1 2 1 这样就可以
# 这里有一个口诀可以运用在函数内
"""
越短的越简单的越靠前
越长的越复杂的越靠后
但是遇到下列的情况除外
同一个形参在调用的时候不能多次赋值
"""
默认参数/关键字参数
在定义函数的时候我们如果已经给括号内填写了形参(变量名+赋值符号+数据值:关键字传参),这就叫默认参数,也叫关键字参数,这种情况下,用户可以选择传输内容也可以不传输内容
# 如果不传输实参,那么函数会使用已经定义好的形参(变量名+赋值符号+数据值:关键字传参)
# 如果传输实参,那么函数会使用传输的实参(变量名+赋值符号+数据值:关键字传参)
def subd(name, password, sex='男性'):
print(name, password, sex)
subd('小明', 222) # 小明 222 男性 如果没有在实参中填写sex内容那么就会根据形参已经定义好的内容输出
subd('小明', 222, '女性') # 小明 222 女性 如果在实参中填写了sex参数内容,那么就输出实参填写的内容
# def subd( sex='男性',name, password,):
# print(name, password, sex)
# subd()
# 默认参数注意事项:
# 默认参数必须在位置参数之后 # non-default argument follows default argument
# 默认参数的值仅在函数定义阶段被赋值一次
可变长参数
1.可变长形参
'''
参数的长度可变指的是在调用函数时,实参的个数可以不固定,而在调用函数时,实参的定义无非是按位置或者按关键字两种形式,这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数
'''
1.
可变长形参我们在定义函数的时候函数名括号内应(*+变量名)---用于接收多余位置参数!!!!!
* 号用于接收多余的位置参数 组织成元组赋值给*号后面的变量名
例:
def subd(a,b,*args):
print(a, b, args)
subd(1,2,3,4,5,6,7) # 输出结果为1 2 (3, 4, 5, 6, 7)
如果没有接收到多余的关键字参数,那么args 就为()空元组
我们在实参里面写了很多数据值,根据位置参数对应了a,b剩余的数据就被*号接收并且转为元组形式赋值给变量 args
def subd(*args,a,b):
print(a, b, args)
subd(1,2,3,4,5,6,7) # 报错,因为*号已经把所有填写的实参接收并赋值给args了,a,b没有赋值 所以会报错
# 所以如果我们在形参中有位置参数,那么我们还是按照位置参数的定义,从左往右依次来,如果实参没有写参数那么就会报错!
2.
可变长形参我们在定义函数的时候函数名括号内应(**+变量名)---用于接收多余关键字参数!!!!!
** 号用于接收多余的关键字参数 组织成字典赋值给**号后面的变量名
例:
def subd(a,b,**kwargs):
print(a,b,kwargs)
subd(a = 1,b = 2,c = 3,d = 4) # 输出 1 2 {'c':3,'d':4}
多余的关键字参数被**号接收并赋值给kwargs
如果没有接收到多余的关键字参数,那么kwargs 就为{}空字典
def subd(a,**kwargs):
print(a,kwargs)
subd() # 报错 位置传参处必须有写参数
subd(1) # 1 {}
subd(1,c= 2) # 1 {'c': 2} 多余的关键字参数被**取出并赋值给kwargs
3.组合使用
def subd(*args,**kwargs)
print(arg,kwargs)
subd(11,22,33) # (11,22,33){}
subd(a = '小明',b = '王刚',11,22) # 报错:位置参数跟在关键字参数后面
*与**它们都是先从实参传递的数据进行遍历,遍历后并进行赋值
"""
由于*和**在函数的形参中使用频率很高 后面跟的变量名推荐使用
*args
**kwargs
def index(*args,**kwargs):pass
"""
2.可变长实参
1.在实参中* 和**就相当于把数据值取出并赋值给给符号后面的变量名
2.* 类似于for循环取值
3.** 类似于字典打变成关键字参数的形式传递给函数
* () 只能按位置传参
** {} 只能按关键字传参
当它们混合使用时,** 要在 * 的后面
# 1. ** 必须放在 * 的后面
def func1(*args, **kwargs):
print(args, **kwargs)
# 2. 参数和动态参数混合时,动态参数只能放在最后。
def func2(a1, a2, a3, *args, **kwargs):
print(a1, a2, a3, args, **kwargs)
def index(a,b,c):
print(a,b,c)
l1 = [11, 22, 33]
t1 = (33, 22, 11)
s1 = 'tom'
se = {123, 321, 222}
d1 = {'username': 'jason', 'pwd': 123, 'age': 18}
index(*l1) 11,22,33
index(*t1) 33, 22, 11
index(*s1) 't','o','m'
index(*se) 123 222 321
index(*d1) 'username','pwd','age'
命名关键字传参
'''形参必须按照关键字参数传值>>>:命名关键字参数'''
def index(name, *args, gender='male', **kwargs):
print(name, args, gender, kwargs)
index('jason',1,2,3,4,a=1,b=2) #
index('jason', 1, 2, 3, 4, 'female', b=2)
名称空间及作用域
# 名称空间我们可以理解为用来存储变量名与数据值绑定关系的地方
我们可以把它分为三部分:
1.内置名称空间
# 内置名称空间就是我们在启动python解释器时它会自动生成一块区域存放内置名字,关闭时销毁
2.全局名称空间
# 全局名称空间是在我们运行python文件的时候创建内存空间,存储此py文件内的名称,运行时销毁
3.局部名称空间
# 局部名称空间是在函数体代码运行的时候创建,函数体代码结束则销毁
它们的作用域:
内置名称空间
# 解释器级别的全局有效
全局名称空间
# py文件级别的全局有效
局部名称空间
# 函数体代码内有效
名字查找顺序
我们看名字查找顺序的时候,要先确认查找时的空间
根据空间的不同,名称顺序也不同
1.当我们在局部名称空间的时候
局部名称空间>>>>> 全局名称空间 >>>>> 内置名称空间
2.当我们在全局名称空间中的时候
全局名称空间>>>>>内置空间名称
ps:其实名字的查找顺序是可以被打破的
查找顺序案例
1.相互独立的局部名称空间默认不能够互相访问
def func1():
name = 'jason'
print(age)
def func2():
age = 18
print(name)
2.局部名称空间嵌套
先从自己的局部名称空间查找 之后由内而外依次查找
函数体代码中名字查找顺序在函数定义阶段已经固定了
例:
x = '干饭了'
def func1():
x = 1
def func2():
x = 2
def func3():
x = 3
print(x)
func3()
func2()
func1()
练习题及答案
判断下列money的值是多少并说明理由 思考如何修改而不是新增绑定关系
money = 100
def index():
money = 666
print(money)
100
因为 print(money)时,money = 100 跟函数没关系,没有调用函数
money = 100
def func1():
money = 666
def func2():
money = 888
func2()
print(money) 因为func2()函数调用在def嵌套里面而 print的时money 优先找全局名字
100