函数的进阶
一.函数的动态参数
1.*args 位置参数动态传参
在参数位置编写*表示接受任意内容
def a(*food):
print(food)
a('福建人','老鼠','猫')
a('虎鞭','牛鞭')
a('蝗虫')
a()
结果:
('福建人','老鼠','猫')
('虎鞭','牛鞭')
('蝗虫')
()
def chi(*food):
print("我要吃", food)
chi("大米饭", "小米饭")
结果:
我要吃 ('大米饭', '小米饭') # 多个参数传递进去. 收到的内容是元组tuple
注意:动态接受参数的时候要注意,动态参数必须在位置参数后面
def chi(*food, a, b):
print("我要吃", food, a, b)
# chi("大米饭", "小米饭", "黄瓜", "茄子") # 错
chi("大米饭", "小米饭", a="黄瓜", b="茄子") # 必须⽤用关键字参数来指定
def chi(a, b, *food):
print("我要吃", a, b, food)
chi("大米饭", "小米饭", "馒头", "面条") # 前两个参数用位置参数来接收, 后面的参数用 动态参数接收
默认值参数
def chi(a, b, c='馒头', *food):
print(a, b, c, food)
chi("香蕉", "菠萝") # 香蕉 菠萝 馒头 () ,默认值生效
chi("香蕉", "菠萝", "葫芦娃") # 香蕉 菠萝 葫芦娃 () ,默认值不生效
chi("香蕉", "菠萝", "葫芦娃", "口罩") # 香蕉 菠萝 葫芦娃 ('口罩',) ,默认值不生效
默认值在动态参数之前只有一种情况才可生效,所以:
def chi(a, b, *food, c="娃哈哈"):
print(a, b, food, c)
chi("香蕉", "菠萝") # 香蕉 菠萝 () 娃哈哈 ,默认值生效
chi("香蕉", "菠萝", "葫芦娃") # 香蕉 菠萝 ('葫芦娃',) 娃哈哈 ,默认值生效
chi("香蕉", "菠萝", "葫芦娃", "口罩") # 香蕉 菠萝 ('葫芦娃', '口罩') 娃哈哈 ,默认值⽣生效
这时所有的默认值都生效了,如果不使用关键字传参,修改默认值,你的默认值是永远都会有效的
2.**kwargs 关键字参数动态传参
def chi(**food):
print(food)
chi(广东人 = '福建人',山东人 = '大葱',东北人 = '大蒜',四川人 = '辣椒')
结果:
{'广东人': '福建人', '山东人': '大葱', '东北人': '大蒜', '四川人': '辣椒'}
def func(**kwargs):
print(kwargs)
func(a=1, b=2, c=3)
func(a=1, b=2)
结果:
{'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2}
这时候接受的是一个字典(dict)
在函数调用的时候,如果先给出关键字参数,则整个参数列表会报错
def func(a, b, c, d):
print(a, b, c, d)
# 关键字参数必须在位置参数后⾯面, 否则参数会混乱
func(1, 2, c=3, 4)
所以关键字参数必须在位置参数后面,由于实参是这个顺序,所以形参接受的时候也是这个顺序,
也就是说位置参数必须在关键字参数前面,动态接收关键字参数也要在后面
顺序:位置参数,*args,默认值,**kwargs
def func(*args, **kwargs) #无敌传参
pass
接受所有的参数:
def func(*args, **kwargs):
print(args, kwargs)
func("麻花藤","马晕",wtf="胡辣汤")
形参的位置: *,**:聚合
实参的位置: *,**:打散
def fun(*args): print(args) lst = [1, 4, 7] fun(lst[0], lst[1],lst[2]) fun(*lst) # 可以使用*把一个列表按顺序打散 s = "臣妾做不到" fun(*s)
在实参的位置上给一个序列,列表,可迭代对象前面加一个*表示把这个序列打散
在形参的位置上的*表示把接收到的参数组和成一个元组
如果是字典,也可以进行打散操作,只不过需要两个*.
def fun(**kwargs): print(kwargs) dic = {'a':1, 'b':2} fun(**dic) # **dic: 'a' = 1,'b' = 2
3.函数的注释
def chi(food, drink):
"""
这里是函数的注释,先写一下当前这个函数是干什么的,比如我这个函数就是一个吃
:param food: 参数food是什么意思
:param drink: 参数drink是什么意思
:return: 返回的是什么东东
"""
print(food, drink)
return "very good"
二.命名空间
1.内置名称空间 --> 存放Python解释器为我们提供的名字,list,tuple,str,int这些都是内置命名空间
2.全局名称空间 --> 我们直接在py文件中,函数外声明的变量都属于全局命名空间
3.局部名称空间 --> 在函数中声明的变量会放在局部命名空间
4.加载顺序:内置命名空间 --> 全局命名空间 --> 局部命名空间(函数被执行的时候)
取值顺序:局部命名空间 --> 全局命名空间 --> 内置命名空间
a = 10
def func():
a = 20
print(a)
func() # 20
a = 10 # 全局名称空间中的内容
def fn(): # fn也在全局名称空间
a = 20 # 局部名称空间
print(a)
def gn():
print(a)
fn()
gn()
结果:
20
10
作用域:
(1).全局作用域:内置+全局
(2).局部作用域:局部(函数被调用)
globals() 查看全局中的内容
locals(): 查看当前作用域中的内容
a = 10
def func():
a = 40
b = 20
def abc():
print("哈哈")
print(a, b) # 这里使用的是局部作用域
print(globals()) # 打印全局作用域中的内容
print(locals()) # 打印局部作用域中的内容
func()
三.函数嵌套
函数可以互相嵌套
def outer():
print("哈哈")
def inner_1():
print("呵呵")
def inner_1_1():
print("嘻嘻")
inner_1_1()
print("吼吼")
def inner_2():
print("嘿嘿")
inner_2()
inner_1()
outer()
结果:
哈哈
嘿嘿
呵呵
嘻嘻
吼吼
四.global和nonlocal
global:在局部访问全局中的内容
a = 100
def func():
global a # 加了个global表示不再局部创建这个变量,而是直接使用全局的a
a = 28
print(a)
func()
print(a)
结果:
28
28
a = 10 # 全局变量本身就是不安全的, 不能随意修改, 闭包
def func():
global a # 1. 可以把全局中的内容引入到函数内部 , 2. 在全局创建一个变量
# a = 20
a += 10 # a = a+10
print(a)
func()
print(a)
global表示,不在使用局部作用域中的内容,而改用全局中的变量
lst = ["麻花藤", "刘嘉玲", "詹姆斯"]
def func():
lst.append("马云") # 对于可变数据类型可以直接进行访问,但是不能改地址说白了就是不能赋值
print(lst)
func()
print(lst)
结果:
['麻花藤', '刘嘉玲', '詹姆斯', '马云']
['麻花藤', '刘嘉玲', '詹姆斯', '马云']
nonlocal:在局部寻找外层函数中离他最近的那个变量
a = 10
def outer():
def inner(): # 在inner中改变a的值
nonlocal a # 寻找外层函数中离他最近的那个变量
a = 20
inner()
outer()
结果:
报错
nonlocal只能在局部作用域中,调用父级命名空间中的变量,找不到全局中的变量
a = 10
def func1():
a = 20
def func2():
nonlocal a
a = 30
print(a)
func2()
print(a)
func1()
结果
加了nonlocal
30
30
不加nonlocal
30
20