python函数(二)
一.函数参数----动态传参
之前我们说过了传参,如果我们需要给一个函数传参,而参数又是不确定的,或者我给一个函数传很多参数,我的形参就要写很多,很麻烦,怎么办呢.我们可以考虑使用动态参数.
形参的第三种:动态参数
动态参数分成两种:
1.动态的接收位置参数
首先我们先回顾下位置参数,位置参数,按照位进行传参
1 def chi(quality_food, junk_food): 2 print("我要吃", quality_food, junk_food) 3 chi("大米饭", "小米饭") # "大米饭"传递给quality_food "小米饭"传递给junk_food 4 按照位置传
现在问题来了.我想吃任意的食物,数量是任意的,食物也是任意的.这时我们就要用到动态参数了.
在参数位置编写*表示接收任意内容
1 def chi(*food): 2 print("我要吃", food) 3 chi("大米饭", "小米饭") 4 结果: 5 我要吃 ('大米饭', '小米饭') # 多个参数传递进去,收到的内容是元组tuple
动态接收参数的时候要注意:动态参数必须在位置参数后面
1 def chi(*food, a, b): 2 print("我要吃", food, a, b) 3 chi("大米饭" "小米饭" "黄瓜" "茄子")
这时候程序运行会报错.因为前面传递进去的所有位置参数都被*food接收了.a和b永远接收不到参数
所以必须改写成下面的代码
1 def chi(*food, a, b): 2 print("我要吃", food, a, b) 3 chi("大米饭", "小米饭", a="黄瓜", b="茄子") # 必须用关键字参数来指定
这个时候a和b就有值了,但是这样写位置参数就不能用了,所以我们要先写位置参数,然后再用动态参数
1 def chi(a, b, *food): 2 print("我要吃", a, b, food) 3 chi("大米饭", "小米饭", "馒头", "面条") # 前两个参数⽤位置参数来接收,后面的参数用动态参数接收
默认参数
1 def chi(a, b, c='馒头', *food): 2 print(a, b, c, food) 3 4 chi("香蕉", "菠萝") # 香蕉 菠萝 馒头 (). 默认值生效 5 chi("香蕉", "菠萝", "葫芦娃") # 香蕉 菠萝 葫芦娃 () 默认值不生效 6 chi("香蕉", "菠萝", "葫芦娃", "口罩") # 香蕉 菠萝 葫芦娃 ('口罩',) 默认值不生效 7
我们发现默认参数写在动态参数前面.默认值只有一种情况可能会生效.
1 def chi(a, b, *food, c="娃哈哈"): 2 print(a, b, food, c) 3 4 chi("香蕉", "菠萝") # 香蕉 菠萝 () 娃哈哈 默认值生效 5 chi("香蕉", "菠萝", "葫芦娃") # 香蕉 菠萝 ('葫芦娃',) 娃哈哈 默认值生效 6 chi("香蕉", "菠萝", "葫芦娃", "口罩") # 香蕉 菠萝 ('葫芦娃', '口罩') 娃哈哈 默认值生效
这个时候我们发现所有的默认值都生效了.这个时候如果不给出关键字传参.那么你的默认值永远都生效的.
顺序:位置参数,动态参数*,默认值参数
2.动态接收关键字参数
在python中可以动态的位置参数,但是*这种情况只能接收位置参数无法接收关键字参数.
在python中使用**来接收动态关键字参数
1 def func(**kwargs): 2 print(kwargs) 3 func(a=1, b=2, c=3) 4 func(a=1, b=2) 5 结果: 6 {'a': 1, 'b': 2, 'c': 3} 7 {'a': 1 'b': 2}
这个时候接收的是一个dict
顺序的问题,在函数调用的时候,如果先给出关键字参数,则整个参数列表会报错
1 def func(a, b, c, d): 2 print(a, b, c, d) 3 # 关键字参数必须在位置参数后⾯, 否则参数会混乱 4 func(1, 2, c=3, 4)
所以关键字参数必须在位置参数后面,由于实参是这个顺序.所以形参接收的时候也是这个顺序,也就是说位置参数必须在关键字参数前面,动态接收关键字参数也要在后面
最终顺序:
位置参数>*args>默认值参数>**kwargs
这四种参数可以任意的进行使用.
如果想接收所有的参数:
1 def func(*args, **kwargs): 2 print(args, kwargs) 3 func("麻花藤","马云",wtf="胡辣汤")
动态参数的另一种传参方式(打散):
1 def fun(*args): 2 print(args) 3 lst = [1, 4, 7] 4 fun(lst[0], lst[1], lst[2]) 5 fun(*lst) # 可以使用*把一个列表按顺序打散 6 s = "臣妾做不到" 7 fun(*s) 字符串也可以打散 (可迭代对象)
在实参位置上给一个序列,列表,可迭代对象前面加个*表示把这个序列按顺序打散.
在形参位置上的*表示把接收到的参数组合成一个元组
如果是一个字典,那么也可以打散.不过需要用两个*
1 def fun(**kwargs): 2 print(kwargs) 3 dic = {'a':1, 'b':2} 4 fun(**dic)
函数的注释
1 def chi(food, drink): 2 """ 这里是函数的注释, 先写一下当前这个函数是干什么的, 比如我这个函数就是一个吃 3 :param :param food: 参数food是什么意思 4 :param :param drink: 参数drink是什么意思 5 :return :return: 返回的是什么东东 6 """ 7 print(food, drink) 8 return "very good"
二.命名空间
我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读如内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。
等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量回储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。
命名空间一共分为三种:
全局命名空间
局部命名空间
内置命名空间
*内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。
三种命名空间之间的加载与取值顺序:
加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
取值顺序:
在局部调用:局部命名空间->全局命名空间->内置命名空间
在全局调用:全局命名空间->内置命名空间
综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。
作用域
作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。
全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
局部作用域:局部名称空间,只能在局部范围内生效
global关键字,nonlocal关键字。
global:
1,声明一个全局变量。
2,在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。
1 def func(): 2 global a 3 a = 3 4 func() 5 print(a) 6 7 8 count = 1 9 def search(): 10 global count 11 count = 2 12 search() 13 print(count) 14 15 global关键字举例
ps:对可变数据类型(list,dict,set)可以直接引用不用通过global。
1 li = [1,2,3] 2 dic = {'a':'b'} 3 4 def change(): 5 li.append('a') 6 dic['q'] = 'g' 7 print(dic) 8 print(li) 9 change() 10 print(li) 11 print(dic) 12 13 对于可变数据类型的应用举例
nonlocal:
1,不能修改全局变量。
2,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
1 def add_b(): 2 b = 42 3 def do_global(): 4 b = 10 5 print(b) 6 def dd_nonlocal(): 7 nonlocal b 8 b = b + 20 9 print(b) 10 dd_nonlocal() 11 print(b) 12 do_global() 13 print(b) 14 add_b() 15 16 nonlocal关键字举例
三.函数的嵌套
1.只要是遇见了()就是函数的调用.如果没有()就不是函数的调用
2.函数的执行顺序
函数的嵌套调用
1 def max2(x,y): 2 m = x if x>y else y 3 return m 4 5 def max4(a,b,c,d): 6 res1 = max2(a,b) 7 res2 = max2(res1,c) 8 res3 = max2(res2,d) 9 return res3 10 11 # max4(23,-7,31,11) 12 13 函数的嵌套调用 14 15 函数的嵌套调用
函数的嵌套定义
1 def f1(): 2 print("in f1") 3 def f2(): 4 print("in f2") 5 6 f2() 7 f1() 8 ########### 9 def f1(): 10 def f2(): 11 def f3(): 12 print("in f3") 13 print("in f2") 14 f3() 15 print("in f1") 16 f2() 17 18 f1() 19 20 函数的嵌套定义
函数的作用域链:小范围作用域可以使用大范围的变量,但是反之不行,他是单向的。
def f1(): a = 1 def f2(): def f3(): print(a) f3() f2() f1() ################ def f1(): a = 1 def f2(): a = 2 f2() print('a in f1 : ',a) f1() 作用域链应用举例
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步