函数进阶

内容一览:

动态参数 *args **kwargs
名称空间(局部,全局,内置)
作用域:全局作用域,局部作用域
加载顺序与取值顺序
内置函数:globals() locals()
关键字: global nonlocal
函数的嵌套(高阶函数)

函数的初识:
封装一个功能
def 函数名():
函数体

函数的返回值:return
1.结束函数
2.返回给执行者值

return ---> 返回 None
return单个值 ---> 返回单个值
return多个值 ---> 返回一个元组

函数的参数:
实参:
1.位置参数——必须与形参角度的位置从左到右一一对应
2.关键字参数——必须一一对应,可以不按顺序
3.混合参数——位置参数必须在关键字参数前面

形参:
1.位置参数——必须与实参角度的位置从左到右一一对应
2.默认参数——必须一一对应
3.动态参数——*args, **kwargs


1 # 动态参数 *args **kwargs
2 
3 def func(*args, **kwargs): # 这句表示在函数的定义时,*表示函数的聚合
4     print(args)
5     print(kwargs)
6 func(1, 2, 3, 4, 5, "alex", name="taibai", sex="")
7 
8 # (1, 2, 3, 4, 5, 'alex')
9 # {'name': 'taibai', 'sex': '男'}
 1 # *的魔性用法
 2 
 3 # 写一个函数,可以传入多个列表,把列表里的所有元素一个一个添加到args里面
 4 
 5 def func(*args, **kwargs):
 6     print(args)
 7 set = func(*[1, 2, 3], *(1, 2, 3), *"askj")     # 函数调用时,*代表打散
 8 print(set)
 9 
10 # (1, 2, 3, 1, 2, 3, 'a', 's', 'k', 'j')
11 # None
12 
13 
14 def func(**kwargs):
15     print(kwargs)
16 func(**{"name": "alex"}, **{"age": 16})
17 
18 # {'name': 'alex', 'age': 16}
19 
20 
21 def func(*args, **kwargs):
22     print(args)
23     print(kwargs)
24 func(*[1, 2, 3], *"adj", **{"name": "alex"}, **{"age": 16})
25 
26 # (1, 2, 3, 'a', 'd', 'j')
27 # {'name': 'alex', 'age': 16}
28 
29 # 总结
30 # 在函数定义时,* **代表聚合
31 # 在函数调用时,* **代表打散
 1 # 特殊用法
 2 a, b, *args = [i for i in range(20)]
 3 print(a, b, args)
 4 # 0 1 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
 5 
 6 # 小 bug
 7 
 8 def func(*args, **kwargs):
 9     print(args)
10     print(kwargs)
11 func(*[1, 2, 3], *"adj", **{1: "alex"}, **{"age": 16})
12 
13 # 这里如果字典里的key是数字就不行,必须是字符串
14 # 比如这样就可以
15 
16 def func(*args, **kwargs):
17     print(args)
18     print(kwargs)
19 
20 func(*[1, 2, 3], *"adj", **{"name": "alex"}, **{"age": 16})
21 
22 # (1, 2, 3, 'a', 'd', 'j')
23 # {'name': 'alex', 'age': 16}
 1 # 形参的顺序问题
 2 # 下面这些的顺序怎么排?
 3 # a, b, c, sex="男", *args, **kwargs
 4 
 5 # 位置参数一定要在默认参数的前面
 6 def func(a, b, sex=""):
 7     print(a)
 8     print(b)
 9     print(sex)
10 func(1, 2, sex="") # 这里也可以把 sex= 去掉
11 
12 # 1
13 # 2
14 #
15 
16 
17 def func(a, b, *args, sex=""):
18     print(a)
19     print(b)
20     print(args)
21     print(sex)
22 func(1, 2, "", "非男")
23 
24 # 1
25 # 2
26 # ('女', '非男')
27 #
28 
29 
30 def func(a, b, *args, sex=""):
31     print(a)
32     print(b)
33     print(args)
34     print(sex)
35 func(1, 2, "", "非男", sex="")
36 
37 # 1
38 # 2
39 # ('女', '非男') # 都传递给了args
40 #
41 
42 
43 def func(a, b, *args, sex="", **kwargs):
44     print(a)
45     print(b)
46     print(args)
47     print(sex)
48     print(kwargs)
49 func(1, 2, "", "非男", sex="", name="alex")
50 
51 # 1
52 # 2
53 # ('女', '非男')
54 #
55 # {'name': 'alex'}
56 
57 
58 # 总结(很重要):
59 # 形参的排序 ---> 位置参数 > *args > 默认参数 > **kwargs
程序开始运行时是逐行解释,从上往下运行
遇到了初始化对象命令时,在内存中存放一个变量与值的对应关系的空间,称为全局名称空间

def func():
sex = ""
func()

程序走到这个函数时,看到函数的定义,会把函数名与函数体的对应关系在内存中存放一个命名空间,里面没有任何东西
遇到函数调用时,会在内存中再开辟一个临时的名称空间,存放的是函数体里面的变量与值(sex=""),该临时名称空间会随着函数的结束而消失
因此在 func() 下一行加 print(sex)时没有结果
因为函数已经结束,这个临时名称空间(局部名称空间)已经消失了

全局名称空间:存放的是py文件中变量与值的对应关系
局部名称空间:临时存放的是函数体里面的变量与值的对应关系
内置名称空间:内置函数,关键字等等,比如 print(), input()
作用域,分为全局作用域与局部作用域
全局作用域:全局名称空间,内置名称空间
局部作用域:局部名称空间

加载顺序——加载到内存的顺序
内置名称空间 ---> 全局名称空间 ---> 局部名称空间
解释器加载到内存中才能写代码,这个时候内置函数已经一起加进内存去了,所以内置名称空间是最先加载的
接下来运行程序,肯定是先加载全局名称空间,遇到函数时才加载局部名称空间
 1 # 取值顺序(就近原则)
 2 
 3 def func():
 4     name = "flash"
 5     print(name)
 6 func()    # flash
 7 
 8 name = "alex"
 9 def func():
10     name = "flash"
11     print(name)
12 func()    # flash
13 # 这里 print(name)是先打印里面的name 对应的变量——即所谓的就近原则
14 
15 name = "alex"
16 def func():
17     # name = "flash"
18     print(name)
19 func()    # alex
20 # 这时局部名称空间里已经没了name这个变量对应的值,所以它就在外面找
21 
22 input = "alex"
23 def func():
24     print(input)
25 func()    # alex
26 
27 # 因此取值顺序是
28 # 局部名称空间 --> 全局名称空间 --> 内置名称空间
 1 # LEGB 原则,也是就近原则
 2 
 3 name = "三包"
 4 def func():
 5     name = "alex"
 6     # print(name)
 7     def inner():
 8         name= "barry"
 9         print(name)
10     inner()
11 # print(name)
12 func()
13 
14 # 因此这个程序运行结果是 barry
15 # 如果把 print(name) 放在 "alex" 下面,结果是 alex
16 # 如果把 print(name) 放在 func() 上面, 则打印 三包
 1 # 内置函数: globals() locals()
 2 
 3 name = "alex"
 4 age = 16
 5 
 6 def func():
 7     name = "barry"
 8     age = 18
 9 
10 print(globals())
11 print(locals())
12 
13 # {'__name__': '__main__',..., 'name': 'alex', 'age': 16}
14 
15 # {'__name__': '__main__',..., 'name': 'alex', 'age': 16}
16 
17 # globals() 打印结果是一个字典,里面内容很多,包含全局作用域的所有内容
18 # locals() 返回的是当前作用域的内容
19 
20 name = "alex"
21 age = 16
22 
23 def func():
24     name = "barry"
25     age = 18
26     print(globals())
27     print(locals())
28 func()
29 
30 # {'__name__': '__main__',..., 'name': 'alex', 'age': 16}
31 
32 # {'age': 18, 'name': 'barry'}
33 # 这里发现 locals() 的结果出来了,因为在当前作用域有内容
34 
35 name = "alex"
36 age = 16
37 
38 def func():
39     name = "barry"
40     age = 18
41     def inner():
42         print(globals())
43         print(locals())
44     inner()
45 func()
46 
47 # {'__name__': '__main__',..., 'name': 'alex', 'age': 16}
48 
49 # {}
50 
51 # 注意刚才说的 locals()返回的是当前作用域的内容,而这里的“当前”什么也没有,因此打印空字典
52 
53 # 无论是加载顺序还是取值顺序都是单向不可逆的
 1 # 高阶函数
 2 # 记住一点:代码是从上往下执行的!!!
 3 # 注意观察下面几个函数的对比
 4 
 5 def func1():
 6     print(111)
 7 
 8 def func2():
 9     print(222)
10     func1()
11 
12 def func3():
13     print(333)
14     func2()
15 print(555)
16 func3()
17 print(666)
18 
19 # 555
20 # 333
21 # 222
22 # 111
23 # 666
24 
25 # 先是执行 print(555)
26 # 然后执行函数
27 # 先执行 func3
28 # 执行完后要等 func3 里面的 func2执行
29 # func2 执行后要等里面的 func1 执行
30 # 最后才执行 print(666)
31 
32 def func1():
33     print(1)
34     def inner():
35         print(2)
36         def inner2():
37             print(3)
38     print(4)
39     inner()
40     print(5)
41 func1()
42 
43 # 1
44 # 4
45 # 2
46 # 5
47 
48 # 注意顺序
49 # 先执行 print(1)
50 # 再执行 print(4)
51 # 再执行 innner(),这里只执行 print(2),不执行 inner2()
52 # 最后执行 print(5)
 1 # global nonlocal
 2 # global 可以在局部声明一个变量
 3 
 4 def func():
 5     name = "alex"
 6 func()
 7 print(name)
 8 
 9 # 报错,找不到 name,因为 name 在局部名称空间
10 
11 def func():
12     global name
13     name = "alex"
14 func()
15 print(name)    # alex
16 
17 # 这里 global name 相当于表示不仅在局部有这个变量对应的值,全局也有(同步)
18 
19 age = 46
20 def func():
21     global name
22     name = "alex"
23     name = "barry"
24 func()
25 print(name)    # barry
26 
27 # 在局部改变了这个变量对应的值的话,全局也会同步改变
28 # 这里注意函数执行结束后,全局的变量指向的值在内存中还存在
29 
30 age = 46
31 name = "xxx"
32 def func():
33     global name
34     name = "alex"
35     name = "barry"
36 func()
37 print(name)  # barry
38 
39 # 这里相当于局部可以操控全局
40 
41 age = 46
42 name = "xxx"
43 def func():
44     global name
45     name = "alex"
46     name = "barry"
47 func()
48 name = "aaa"
49 print(name)
50 
51 count = 1
52 def func():
53     count += 1
54 func()
55 
56 # 局部作用域不能对全局作用域的变量进行修改,只能引用
57 
58 count = 1
59 def func():
60     global count
61     count += 1
62 func()
63 print(count)
64 # 说明可以通过设置global就可以修改
65 
66 # global用法总结:
67 # 可以局部作用域声明一个全局变量
68 # 局部作用域不能对全局作用域的变量进行修改,只能引用,但可以通过设置global修改
 1 # nonlocal: 只在python3x中存中
 2 # 子级对父级的操控
 3 
 4 # 不能操控全局变量
 5 name = "alex"
 6 def func():
 7     nonlocal name
 8     print(name)
 9 func()
10 
11 # 内存函数可以引用外出函数的变量
12 def wrapper():
13     name = "alex"
14     def inner():
15         print(name)
16     inner()
17 wrapper()
18 
19 # 内层函数只能引用外层函数的变量,不能改变
20 def wrapper():
21     name = "alex"
22     def inner():
23         name += "b"
24     inner()
25 wrapper()
26 # 报错
27 
28 # 非要修改,只能用nonlocal
29 def wrapper():
30     name = "alex"
31     def inner():
32         nonlocal name
33         name += "b"
34         print(name)
35     inner()
36 wrapper()
37 
38 
39 def wrapper():
40     name = "alex"
41     def inner():
42         nonlocal name
43         name += "b"
44         print(name)
45     print("1", name)
46     inner()
47     print("2", name)
48 wrapper()
49 
50 # 先执行 print("1", name)
51 # 再执行 inner(),name变量指向的值已经变为 alexb
52 # 最后执行 print("2", name)时,因为前面已经执行 inner(),因此从第二步开始,往下所有的name变量指向的值都变成了 alexb
原文地址:https://www.cnblogs.com/shawnhuang/p/10198125.html