一、动态参数
当我们有很多参数时,一个一个的去写形参,很感觉很麻烦,哪有什么简便的万能形参吗?答案是有的,那就是我们的动态参数!!!
昨天我们站在形参的角度可以把参数分为:位置参数和默认参数,今天我们将学习第三种:动态参数
1、动态接受位置参数
首先我们回顾一下位置参数,位置参数,按照位置进行传参(一定要注意实参与形参一一对应)
def chi(quality_food, junk_food): print("我要吃", quality_food, junk_food) chi("⼤⽶饭", "⼩米饭") # "⼤米饭"传递给quality_food "小米饭"传递给junk_food 按照位置传 现在问题来了. 我想吃任意的食物. 数量是任意的, 食物也是任意的. 这时我们就要用到 动态参数了. 在参数位置编写*表示接收任意内容 def chi(*food): print("我要吃", food) chi("⼤米饭", "⼩米饭") 结果: 我要吃 ('⼤米饭', '小米饭') # 多个参数传递进去. 收到的内容是元组tuple
需要注意的是这里将说有的位置参数打包成一个人元组赋值给food,所以打印food得到一个元组 动态接收参数的时候要注意: 动态参数必须在位置参数后面 def chi(*food, a, b): print("我要吃", food, a, b) chi("⼤米饭", "⼩米饭", "⻩瓜", "茄⼦") # 这里是要报错的,因为动态参数将所有位置参数接受完了 报错代码:Traceback (most recent call last): File "/Users/sylar/PycharmProjects/oldboy/fun.py", line 95, in <module> chi("⼤米饭", "⼩米饭", "⻩瓜", "茄子") TypeError: chi() missing 2 required keyword-only arguments: 'a' and 'b'
1.2、默认参数,运用关键字参数给出形参,当需要调用修改时才去修改
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、动态接收关键字参数:
在python中可以动态的位置参数, 但是*这种情况只能接收位置参数⽆法接收关键字参数.在python中使⽤用**来接收动态关键字参数
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},将传入的数据转换为字典 需要注意顺序的是:如果先给出关键字参数,则整个参数列表会报错 def func(a, b, c, d): print(a, b, c, d) # 关键字参数必须在位置参数后⾯面, 否则参数会混乱 func(1, 2, c=3, 4) # 一定注意关键字参数在后面 所以关键字参数必须在位置参数后⾯. 由于实参是这个顺序. 所以形参接收的时候也是这 个顺序. 也就是说位置参数必须在关键字参数前面. 动态接收关键字参数也要在后面 参数一定要从两种角度来看,一种是从实参,另一种是从形参上看
最终形参顺序(*): 位置参数 > *args > 默认值参数 > **kwargs 这四种参数可以任意的进⾏行行使⽤用. 如果想接收所有的参数: def func(*args, **kwargs): print(args, kwargs) func("麻花藤","⻢晕",wtf="胡辣汤")
2.1、动态参数的另一种传参方式(聚合与打散)
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} dic_ = {'g':1,'c':3} fun(**dic,**dic_) # 结果{'a': 1, 'b': 2, 'g': 1, 'c': 3},就是把传入的打散 ,实参上打散,把外皮剥了
3、函数的注释:(对函数的功能注释)
def chi(food, drink): """ 这⾥是函数的注释 , 先写⼀下当前这个函数是⼲什么的 , 比如我这个函数就是一个吃 :param :param food: 参数 food 是什么意思 :param :param drink: 参数 drink 是什么意思 :return :return: 返回的是什么东东 """ print(food, drink) return "very good"
二、命名空间
1、在python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就 把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存,
表示这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的. 也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调用和访问的时候, 解释器才会根据函数 内部声明
的变量量来进⾏开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占⽤的空间也会随着函数执行完毕⽽被清空.
def fun(): a = 10 print(a) fun() print(a) # a不不存在了了已经
我们给存放名字和值的关系的空间起⼀个名字叫: 命名空间. 我们的变量在存储的时候就是存储在这片空间中的.
命名空间分类:
1. 全局命名空间--> 我们直接在py⽂文件中, 函数外声明的变量量都属于全局命名空间
2. 局部命名空间--> 在函数中声明的变量会放在局部命名空间
3. 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
加载顺序: 1. 内置命名空间
2. 全局命名空间
3. 局部命名空间(函数被执⾏的时候)
取值顺序: 1. 局部命名空间
2. 全局命名空间
3. 内置命名空间
2、 作⽤域: 作⽤域就是作用范围, 按照生效范围来看分为全局作⽤域和局部作用域
全局作⽤域: 包含内置命名空间和全局命名空间. 在整个⽂件的任何位置都可以使用(遵循 从上到下逐⾏执⾏).
局部作⽤域: 在函数内部可以使⽤.
作⽤域命名空间:
1. 全局作⽤域: 全局命名空间 + 内置命名空间
2. 局部作⽤域: 局部命名空间
我们可以通过globals()函数来查看全局作用域中的内容, 也可以通过locals()来查看局部作用域中的变量和函数信息
a = 10 def func(): a = 40 b = 20 def abc(): print("哈哈") print(a, b) # 这⾥里里使⽤用的是局部作⽤用域 print(globals()) # 打印全局作⽤用域中的内容 包括一些内置函数 print(locals()) # 打印局部作⽤用域中的内容 func()
三、函数的嵌套
1、只要遇见了函数名()就是函数的调用,如果没有就不是函数的调用
2、函数的执行顺序
def fun1(): print(111) def fun2(): print(222) fun1() #从上至下依次执行 fun2() print(111) # 函数的嵌套 def fun2(): print(222) def fun3(): print(666) print(444) fun3() print(888) print(33) fun2() print(555)
四、关键字global和nonlocal
⾸先我们写这样⼀个代码, 先在全局声明一个变量, 然后再局部调⽤用这个变量, 并改变这 个变量的值
a = 100 def func(): global a # 加了个global表示不再局部创建这个变量了. ⽽是直接使⽤全局的a a = 28 print(a) #z注意再次调用的是全局的a,修改时同步到的 func() print(a) # 28 lst = ["麻花藤", "刘嘉玲", "詹姆斯"] def func(): lst.append("⻢云") # 对于可变数据类型可以直接进行访问. 但是不能改地址. 说⽩了. 不能赋值 print(lst) func() print(lst)
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 看看下面代码的结果: a = 1 def fun_1(): a = 2 def fun_2(): global 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) #1,2, 3,4,3,2,3 再看看nonlocal的代码: 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) #1,2,3,4,3,3,1