函数的进阶
函数参数--动态传参
之前我们说过了传参, 如果我们需要给一个函数传参, 而参数又是不确定的. 或者我给一个
函数传很多参数, 我的形参就要写很多, 很麻烦, 怎么办呢. 我们可以考虑使用动态参数.
形参的第三种: 动态参数
动态参数分成两种 :
1.动态接受位置参数
首先我们要回顾一下位置参数,位置参数,按照位置进行传参
现在问题来了. 我想吃任意的食物. 数量是任意的, 食物也是任意的. 这时我们就要就到
动态参数了.
在参数位置编写*表示接收任意内容
def chi(*food): # * 表示的是不定参数. 可以传递任意个信息 参数名是food, 接受到的是元组 print("我要吃", food) chi("一锅大米饭", "一箱辣条", "一桶方便面", "4L可乐") chi("方便面") chi("一大锅米饭", "一小锅小米饭", "一箱辣条", "一桶方便面", "4L可乐")
动态接收参数的时候要注意: 动态参数必须在位置参数后:
这时程序运行会报错. 因为前⾯传递进去的所有位置参数都被*food接收了. a和b永远接收
不到参数
所以必须改写成以下代码:
这个时候a和b就有值了, 但是这样写呢位置参数就不能用了. 所以. 我们要先写位置参数,
然后再用动态参数
如果默认值参数在*args前面. 如果想让默认值生效. *args将永远接不到值 def func( a, b, *args,c = 5): # arguments参数 print(a, b, c, args) func(1,2,3,4,5,6,8,c = 10)
这个时候我们发现所有的默认值都生效了. 这个时候如果不给出关键字传参. 那么你的默
认值是永远都生效的
顺序: 位置参数, 动态参数*, 默认值参数
2. 动态接收关键字参数
在python中可以动态的位置参数, 但是*这种情况只能接收位置参数用法接收关键字参数.
在python中使用**来接收动态关键字参数
def func(**kwargs): # key word arguments print(kwargs) func(a=10, b=20, jay="周杰伦", jj="林俊杰")
这个时候接收的是一个dict
顺序的问题, 在函数调用的时候, 如果先给出关键字参数, 则整个参数列表会报错.
所以关键字参数必须在位置参数后⾯. 由于实参是这个顺序. 所以形参接收的时候也是这
个顺序. 也就是说位置参数必须在关键字参数前面. 动态接收关键字参数也要在后面
最终顺序:位置参数 > *args >默认值参数 >**kwargs
这四个参数可以接受任意使用
如果要接受所有的参数:
# 无敌模式. 所有的参数都能接收 def func(*args, **kwargs): print(args) print(kwargs)
func(1, 2, 5, jj="陶喆", jay="zhoujielun", soup="胡辣汤")
def func(*args): # 在这里. 其实相当于把传进来的参数做了一次聚合, 聚合成一个元组 print(args) lst = "娃哈哈" func(*lst) # 在实参位置 * 表示打散, 打散的是可迭代对象
def func(**kwargs): # ** 把接收到的关键字参数打包(聚合)成字典 print(kwargs) # 一定是字典 dic = {"张无忌": "明教教主", "谢逊": "金毛狮王", "范瑶": "光明右使"} func(张无忌=dic['张无忌'], 谢逊=dic['谢逊'], 范瑶=dic['范瑶']) func(**dic) # 这里的** 是把字典打散. 字典的key作为参数的名字, 字典的值作为参数的值传递给形参
在形参上
1. 位置参数
2. 默认值参数
3. 动态参数
1. *args 位置参数的动态传参. 系统会自动的把所有的位置参数聚合成元组
2. **kwargs 关键字的动态传参. 系统会自动把所有的关键字参数聚合成字典
3. def func(*args, **kwargs): 无敌传参
4. 顺序: 位置参数, *args, 默认值, **kwargs
5. 在使用的时候, 可以任意的进行搭配
4. 在实参上. *, **表示的打散. 在形参. *,** 表示聚合
命名空间:
在python解释器开始执⾏之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就
把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内
存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始
的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数
内部声明的变量来进行开辟变量的内部空间. 随着函数执⾏完毕, 这些函数内部变量占用的空
间也会随着函数执行完毕而被清空.
# 最开始会开辟一个自己的命名空间- 内置名称空间 a = 10 print(a) # 直接在py文件一个层面上是全局名称空间 def func(): hasakey = 10 # 在函数内部属于局部名称空间 func() 加载顺序: 内置 => 全局 => 局部 取值顺序: 局部 => 全局 => 内置
a = 10 # 全局 def func(): a = 20 print(a) func()
作用域: 作用域就是作用范围, 按照生效范围来看分为 全局作用域和局部作用域
全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循
从上到下逐行执行). 局部作用域: 在函数内部可以使用.
作用域命名空间:
1. 全局作用域: 全局命名空间 + 内置命名空间
2. 局部作用域: 局部命名空间
我们可以通过globals()函数来查看全局作⽤域中的内容, 也可以通过locals()来查看局部作用
域中的变量和函数信息
作用域: 变量或者函数的声明周期 全局作用域: 全局名称空间+内置名称空间 局部作用域: 局部名称空间 a = 10 # 全局 def func(): liuwei = "刘伟"
# 1. globals() 查看全局作用域中的所有内容 qiao = "乔峰" def 抗收音机(): bgm = "难念的经" print("娃哈哈") print(locals()) 抗收音机() # print(globals()) # 查看全局作用域中的内容 # print(locals()) # locals()查看的是当前作用域中的内容
函数的嵌套
1. 只要遇见了()就是函数的调用. 如果没有()就不是函数的调用
2. 函数的执行顺序
def func1(): print("我是func1") def func2(): print("我是func2") func1() def func3(): func2() print("我是func3") func3() 这样的代码不是嵌套. 互相调用
函数可以互相嵌套 def outer(): def inner(): print("我是内部") print("我是外部") inner() outer() def outer(): print("我是外面的") def inner_1(): def inner_2(): print("我是里面的2") inner_2() print("我是里面的1") inner_1() print("我是外面的收尾") outer()
关键字 global和nonlocal
首先我们要写这样的一个代码,首先在全局声明一个变量然后再局部调用这个变量,改变这个变量的值
a = 10 def func(): global a #表示在当前作用域中的使用的a是全局中的变量 a = 20 # 所有的a都是外面的了 print(a) # 现在只有看的权利 print(a) # 10 func() # 20 print(a) # 20
def outer(): a = 10 def inner(): nonlocal a # 找的是局部当中, 离他最近的上层的那个变量 a = 20 print(a) print(a) # 10 inner() # 20 print(a) # 20 outer()
深坑:
a = 1 def fun_1(): a = 2 def fun_2(): nonlocal a a = 3 def fun_3(): a = 4 print(a) print(a) fun_3() print(a) print(a) fun_2() print(a) print(a) fun_1() print(a)
# global 引入全局变量, 可以定义全局变量
# nonlocal 引入局部中离他最近的外层变量
def func(): global a # 没有也得有. 自动帮你创建 a = 20 func() print(a)
练习:
# 写函数,接收n个数字,求这些参数数字的和。(动态传参) def func(*n): sum = 0 for el in n: sum += el return sum ret = func(1,2,5,4,7) # 写函数,传入函数中多个实参(均为可迭代对象如字符串,列表,元祖,集合等), # 将每个实参的每个元素依次添加到函数的动态参数args里面. # 例如 传入函数两个参数[1,2,3] (22,33)最终args为(1,2,3,22,33) def func(*args): # str, list, tuple, set print(args) lst = [] for el in args: for e in el: lst.append(e) tu = tuple(lst) print(tu) # func(*"哈哈", *[1,3,5], *(456,789)) # 写函数,传入函数中多个实参(实参均为字典),将每个实参的键值对依次添加到函数的动态参数kwargs里面. # 例如 传入函数两个参数{‘name’:’alex’} {‘age’:1000}最终kwargs为{‘name’:’alex’ ,‘age’:1000} def func(**kwargs): print(kwargs) func(**{"alex": "大帅逼", "wusir":"丑"}) # 9,写函数,接收一个参数(此参数类型必须是可迭代对象),将可迭代对象的每个元素以’_’相连接,形成新的字符串,并返回. # 例如 传入的可迭代对象为[1,'老男孩','武sir']返回的结果为’1_老男孩_武sir’ def func(it): result = "" for el in it: result += str(el) + "_" return result[:-1] for i in range(len(it)): el = str(it[i]) it[i] = el return "_".join(it) print(func([1,"alex","wusiar"])) print("alex"+213) # 写函数,传入n个数,返回字典{‘max’:最大值,’min’:最小值} # 例如:min_max(2,5,7,8,4) 返回:{‘max’:8,’min’:2}(此题用到max(),min()内置函数) def func(*n): return {"最大值":max(n), "最小值":min(n)} print(func(1,8,6,4,5,15,63,35,5)) # 求阶乘 def func(n): sum = 1 for i in range(n, 0, -1): sum *= i return sum print(func(5)) # 写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组 # 例如:[(‘红心’,2),(‘草花’,2), …(‘黑桃’,‘A’)] # 花色:红黑芳草 # 点数: A2345678910 # 笛卡儿积. def func(): hua = ["红心", "黑桃", "梅花", "方块"] dian = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"] result = [] for huase in hua: for dianshu in dian: result.append((huase, dianshu)) return result # # def wrapper(): def inner(): print(666) return inner # 函数名可以作为变量返回 # # fn = wrapper() fn() def extendList(val, list=[]): # 默认值在内存中只会产生一份 list.append(val) return list list1 = extendList(10) print('list1=%s' % list1) list2 = extendList(123, []) print('list2=%s' % list2) list3 = extendList('a') print('list3=%s' % list3) print(id(list1)) print(id(list3)) print(id(list2))