1.初识函数定义与返回
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 函数 2 # 定义函数 3 def my_len(): 4 i = 0 5 for k in s: 6 i += 1 7 return i 8 9 # 函数没有返回值时,函数返回的是None,只写return时返回的也是None,return None也是返回None 10 11 # 函数return返回一个值的情况下:可以返回任何数据类型 12 13 # 函数return返回多个值的情况下(相当于返回了一个元组。元组、列表、字典都是可以解包的):可以多个变量接收,有多少个返回值就用多少个变量接收 14 # 返回多个值用一个变量接收,得到的是一个元组 15 def func2(): 16 return 1, 2, 3 17 18 r = func2() 19 print(r) # (1, 2, 3) 20 21 # 元组、列表、字典都是可以解包的 22 a, b, c = (1, 2, 3) 23 24 a, b, c = [1, 2, 3] 25 26 a, b, c = {'1' : '2', '3' :'4', '5' : '6'} # 随机将键赋值给每个变量 27 28 print(a)
2.函数参数(1)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 函数参数, 形参与实参 2 def my_len1(s): 3 i = 0 4 for k in s: 5 i += 1 6 return i 7 8 s = 'afasfsdf' 9 10 print(my_len1(s))
2.函数参数(2)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 函数参数 2 3 # 没有参数 4 # 定义函数和调用函数时括号里都不写 5 # 有一个参数 6 # 传什么是什么 7 # 有多个参数 8 # 位置参数,参数位置一一对应, 9 # 站在实参的角度上: 10 # 按照形参名(关键字)传参 11 # 混着用:必须先按照位置传参,再按照形参名(关键字)传参 12 # 不能给同一个形参传多个值 13 # 站在形参的角度上 14 # 位置参数:必须传,多传少传都不行 15 # 默认参数:可以不传,如果不传就是用默认的参数,如果传了使用传的 16 # 默认参数:关键字参数: 参数名 = '默认值' 17 # 动态参数: 可以接收任意多个参数 18 #参数名之前加*,习惯为*args,属于位置参数,必须在前,接收的是位置参数,组织成一个元组 19 #参数名之前加**,习惯我**kwargs,属于关键字参数,必须先定义位置参数,在定义关键字参数,传参时也是,先传位置参数,在传关键字参数。 20 # 顺序:位置参数、*args(属于位置参数)、默认参数、**kwargs 21 # 这是函数传参固定的顺序 22 23 24 def my_sum(a, b): #位置参数 25 return a + b 26 27 print(my_sum(1, 2)) # 位置参数,1传个了a,2传给了b 28 29 ret = my_sum(b = 1, a = 2) # 按键形参名(关键字)传参 30 print(ret) 31 32 print(my_sum(2, b = 3)) # 必须先按照位置传参,再按照形参名(关键字)传参 33 34 35 # 定义函数的时候,必须先定义位置参数,后定义默认参数 36 def classmate(name, sex = '男'): # 默认参数:可以不传,如果不传就是用默认的参数,如果传了使用传的 37 print('{} : {}'.format(name, sex)) 38 39 classmate('wuhengyi') 40 classmate('fqq', '女') 41 42 43 44 # 定义函数的时候,动态参数,*是关键字,参数名之前加*,参数名习惯用args 45 # 先定义位置参数,在定义动态参数,在定义默认参数 46 def sum(*args): # args元组的形式 47 n = 0 48 for i in args: 49 n += i 50 return n 51 52 sum(12, 2) 53 sum(1, 2, 3) 54 print(sum(1, 2, 3, 4)) 55 56 57 def func(*args, l = []): 58 print(args) 59 60 func(1, 2, 3 ,'srg', {1: 'a'}, l = ['123', 42]) 61 62 63 # 动态参数,**kwargs,是字典的形式 64 def func1(**kwargs): 65 print(kwargs) 66 67 68 func1(a = 1, b = 2, c = 3) # {'b': 2, 'a': 1, 'c': 3} 69 func1(a = 1, b = 2) # {'b': 2, 'a': 1} 70 func1(a = 1) 71 72 73 # 动态参数有两种,特点是可以接收任意多个参数 74 # *args : 接收的是按照位置传参的值,组织成一个元组 75 # **kwargs : 接收的是按照关键字传参的值,组织成一个字典 76 77 # 先按位置传,在按关键字传。函数传参的规则,先传位置参数,在传关键字参数。所以*args必须在**kwargs之前。 78 # 函数传参固定顺序,位置参数、*args、默认参数、**kwargs 79 def func2(a1, b1, *args, default = 1, **kwargs): 80 print(a1, b1, args, kwargs) 81 82 func2(1, 2, 3, 4, 5, a= '1', b = 'b', c = 'ccc') 83 84 85 # 动态参数的另一种传参方式 86 def func(*args): # 站在形参的角度上,给变量加上*,就是组合所有传进来的值 87 print(args) 88 89 func(1, 2, 3, 4) 90 91 l = [1, 2, 3, 4, 5] 92 func(*l) # 站在实参的角度上给一个序列(list, tuple)加上*,就是将这个序列按照顺序打撒 93 # (1, 2, 3, 4, 5) 94 95 # 动态参数的另一种传参方式 96 def func(**kwargs): 97 print(kwargs) 98 99 dic = {'a' : 1, 'b' :2} 100 101 func(**dic) # {'a': 1, 'b': 2} **可以打散一个字典,将打散的键值对传递给**kwargs,**kwargs将值组合成一个dict 102 103 # 函数的注释,在函数里开始加,先说函数实现了什么的功能,在说参数的参数类型,参数作用,在说返回值 104 def func(): 105 ''' 106 这个函数实现了什么功能 107 参数1:数据类型,作用 108 参数2:数据类型,作用 109 :return: 是字符串或者列表的长度 110 ''' 111 pass 112 113 114 # 函数 115 # 内置函数 116 117 # 自定义函数 118 # 有参数的、没参数的、位置参数、*args、默认参数、**kwargs
补充默认参数
1 # 默认参数, 形参因为是list,list是可变数据类型,在内存中,内存里面的内容是可以变得,相当于指针。形参是dict是一样的 2 def qqxing(l = []): 3 l.append(1) 4 print(l) 5 6 l = [] 7 8 qqxing(l) 9 10 print(l)
3.函数的命名空间
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 命名空间和作用域 2 # 内置命名空间,python内部集成 3 # 就是python解释器一启动就能识别的,如关键字、print、len等等,存储在内置命名空间中 4 # 内置的名字在启动解释器的时候被加载到内存中 5 # 全局命名空间 6 # 在程序从上到下被执行的过程中依次加载到内存中,当前文件,从上到下 7 # 局部命名空间,由函数弄出 8 # 函数内部定义的名字 9 # 当调用函数的时候 才会产生这个名字的命名空间 随着函数执行结束这个名字的局部命名空间消失 10 11 # 12 # 在局部:可以使用全局命名空间的东西(之前),可以使用内置命名空间中的东西 13 # 在全局:可以使用内置空间中的东西,不能使用函数内局部空间的东西 14 # 在内置命名空间中:python是不能使用我们全局和局部的空间的。 15 16 # 三种命名空间之间的加载与取值顺序 17 # 加载顺序:内置命名空间(程序运行前健在)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用函数时才加载) 18 19 # 当再全局命名空间中定义了内置命名空间中同名的名字,在全局会使用全局的定义的 20 # 当局部命名空间中定义了与全局命名空间中同名的名字,在局部会使用局部定义的 21 # 自己命名空间中有的则使用自己的,如果自己命名空间没有的会去上一层命名空间中的 22 23 24 25 # 作用域有两种 26 # 全局作用域,内置命名空间和全局命名空间的都是作用于全局作用域的 27 # 局部作用域 28 29 # 对于不可变数据类型 在局部可查看全局作用域中的变量,但是不能直接修改,如果想要修改,需要global声明 30 a = 1 31 def func5(): 32 global a # 如果在局部想要操作修改全局的,必须在局部中声明全局中的变量 33 a += 1 34 func5() 35 print(a) 36 37 a = 1 38 b = 2 39 def func6(): 40 x = 'aaaa' 41 y = 'bbbb' 42 print(locals()) # 使用locals()可以查看局部作用域空间中所有的变量和对应值,存放到一个dict中展示。在局部显示局部的,在全局显示全局的名字与对应值 43 44 func6() 45 46 print(globals()) # 可以查看全局作用域中的名字域对应值,以dict形式展示
三目运算
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 三目运算 2 3 print(5 if 2 > 1 else 4) # 如果2 大于 1 返回5 否则返回 4 4 5 def max(a, b): 6 return a if a > b else b 7 8 print(max(1, 2))
4.函数的嵌套和作用域链
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 函数嵌套调用 2 3 print(5 if 2 > 1 else 4) # 如果2 大于 1 返回5 否则返回 4 4 5 def max(a, b): 6 return a if a > b else b 7 8 def the_max(x, y, z): 9 c = max(x, y) # 函数嵌套 10 return max(c, z) 11 12 print(the_max(1, 2, 3)) 13 14 15 # 函数嵌套定义 16 17 # 作用域链 18 # 当outer()调用时,outer被执行,执行内容为先在outer局部命名空间定义了一个a,之后又在outer局部命名空间a之后定义了一个函数inner,inner属于outer局部命名空间但在a的后面,定义完inner后,调用了inner,调用inner时发现inner局部空间中没有a,所以去outer的局部命令空间看有没有a,发现有,所以使用了outer局部命名空间中的a 19 def outer(): 20 h = 1 21 def inner(): 22 nonlocal h # nonlocal 只能用于局部变量, 向上一层的局部找h,如果上一层局部有h,则使用h,如果上一层局部没有则继续往上一层局部找,如果局部没有则报错。找离当前函数向上最近一层的局部变量 23 print('inner') 24 h += 1 # 不能用global,global修改的是全局的\ 25 print(h) # 2 26 inner() 27 28 outer() 29 30 31 # 函数名的本质是执行内存地址的一个名字, 32 33 def func(): 34 print(123) 35 36 func2 = func # 函数可以复制,这不就是函数指针的形式 37 func2() 38 l = [func, func2] # 函数名可以作为容器类型的元素 39 print(l) 40 for i in l: # 函数指针数组概念,遍历list中没有一个函数,拿到地址后解引用运行 41 i() 42 43 def func(): 44 print(123) 45 46 def wahaha(f): 47 f() 48 return f 49 50 wahaha(func) # 函数名可以作为函数的参数,很好理解,函数指针传递. 函数可以返回函数名(就是返回内存地址)
5. 闭包
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 # 闭包:肯定是嵌套函数,内部函数用到了外部函数的变量就算是闭包 2 3 # inner() 就是一个闭包 4 # def outer(): 5 # a = 1 6 # def inner(): 7 # print(a) 8 # print(inner.__closure__) # (<cell at 0x000001EFBAD4B198: int object at 0x000000005B545320>,) 打印能看到cell就说明是一个闭包 9 # outer() 10 11 12 # 闭包的正确使用方法,将闭包函数的函数名返回出去,外部定义一个对象去接收者个函数名值,这个对象可以运行这个闭包函数 13 def outer(): 14 a = 5 # 当outer被调用退出后,a的变量值不会因作用域的结束而消失,inner可以随意使用,延长了a的生命周期,可以上inner闭包函数直接使用 15 def inner(): 16 print(a) 17 return inner # 18 inn = outer() # 19 20 inn() # 这种方式调用,会直接掉用inner,inner用到了outer的a,outer的a此时会在内存中,值保留 21 22 23 24 # 闭包的应用, urllib是python自带的模块,就是自带的python文件,那个python文件的名字叫做urllib.py.. 这个urllib模块有很多方法,是关于url请求的 25 # import urllib # 导入urllib模块 26 from urllib.request import urlopen # 从urllib模块中只导入一个方法urlopen. urlopen方法可以打开一个网页 27 28 # www.xiaohua100.cn/index.html 校花网 29 # ret = urlopen('http://www.xiaohua100.cn/index.html').read() # 获取这个网页内容 30 # print(ret) 31 32 # 闭包的应用 33 # 如果不用闭包,下面的这个函数每次调用的时候都会创建一个url,然后填充网址,然后打开这个网页将内容读取出来赋值给ret,每次调用都如此,这样很不好 34 # def get_url(): 35 # url = 'http://www.xiaohua100.cn/index.html' 36 # ret = urlopen(url).read() # 获取这个网页内容 37 # print(ret) 38 39 # 闭包的应用 40 # 下面的这个函数用的了闭包,好处就是我们可以调用一此get_url后,得到inner的函数名地址,之后每次想要打开着个url的时候,直接运行inner这个闭包就行,这个inner闭包的url变量就会直接使用get_url的url 41 def get_url(): 42 url = 'http://www.xiaohua100.cn/index.html' 43 def inner(): 44 ret = urlopen(url).read() # 获取这个网页内容 45 print(ret) 46 return inner 47 48 get_func = get_url() # 以后想要执行inner的时候,直接调用get_func就行,这时url变量不会反复创建被赋值,节省了运行时间 49 50 get_func()