10. 前方高能-函数的进阶
本节主要内容:
1.函数参数—动态传参
2.名称空间,局部名称空间,全局名称空间,作用域,加载顺序
3.函数的嵌套
4.gloabal,nonlocal关键字
一.函数参数—动态传参
之前我们说过了传参,如果我们需要一个函数传参,而参数又不确实的,或者我给一个函数传很多参数,我的形参就要写很多,很麻烦,那怎么办呢,我们可以考虑使用动态参数。
形参的第三种:动态参数
动态参数分为两种:
1.动态接受为止参数
首先我们先回顾一下为止参数,位置参数,按照位置进行传参
def chi(quality_food,junk_food): print("我要吃",quality_food,junk_food) chi("大米饭","小米饭") #“大米饭”传递给quality_food "小米饭"传递给junk_food按照位置传
现在问题来了,我想吃任意食物,数量是任意的,食物也是任意的,这时我们就要用到动态参数了。
在参数位置编写*表示接收任意内容
def chi(*food): print("我要吃",food) chi("大米饭","小米饭") 结果: 我要吃("大米饭","小米饭") #多个参数传递进去,收到的内容是元祖tuple
动态接受参数的时候要注意:动态参数必须在位置参数后面
def chi(*food, a, b): print("我要吃", food, a, b) chi("大米饭", "小米饭", "黄瓜", "茄子")
这时候程序会报错,因为前面传递进去的所有位置参数都被*food接受了,a和b永远接受不到参数
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'
所以必须修改成下面代码:
def chi(*food,a,b): print("我要吃",food,a,b) chi("大米饭","小米饭",a="黄瓜",b="茄子")#必须用关键字参数来指定
这时候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("香蕉","菠萝","葫芦娃","口罩") #香蕉 菠萝 ("葫芦娃","口罩")娃哈哈 默认值生效
这时候我们发现所有的默认值都生效了,这时候如果不给出关键字传参,那么你的默认值是永远都生效的。
顺序:位置参数,*动态参数,默认值参数 ,**kwargs
顺序: 位置参数 *args 默认值参数 **kwargs
2.动态接收关键字参数
在python中可以动态的位置参数,但是*args这种情况只能接收位置参数无法接受关键字参数。
在python中使用**kwargs来接收动态关键字参数
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): 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)
函数的注释:
def chi(food,drink): """ 这里是函数的注释,先写一下当前这个函数是干什么的,比如我这个函数就是一个吃 :param food: 参数food是什么意思 :param drink: 参数drink是什么意思 :return: 返回的是什么东东 """ print(food,drink) return "very good"
二.命名空间
在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.内置命名空间
a = 10 def dunc(): a = 20 print(a) dunc() #20
作用域:作用域就是作用范围,按照生效范围来看分为全局作用域和局部作用域
全局作用域:包含内置命名空间和全局命名空间,在整个文件的任何位置都可以使用(遵循从上到下逐行执行).局部作用域:在函数内部可以使用。
作用域名空间:
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 = 10 # def func(): # global a # 这里用的a是全局的 # a = 20 # print(a) # func() # print(a) # 20 20 # a = 100 # def func(): # global a #加了个global 表示不再局部创建这个变量,而是直接使用全局的a # a = 28 # print(a) # func() # print(a) # 28 28
global 表示,不再使用局部作用域中的内容,而改变全局作用域中的变量
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(): 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
这样的程序如果能分析明白,那么作用域,global ,nonlocal 就没问题了