10_Python的函数function
1.函数的概述
1.函数是可以重复执行的语句块且可以重复调用,函数封装了可重复执行的语句提高了语句的可重复性
2.函数的参数和返回值的作用流程图: https://www.processon.com/view/link/5ec2a0940791290fe06ea284
3.面向过程开发流程图: https://www.processon.com/view/link/5ec32177e0b34d5f26172759
4.函数定于语句的完整语法
[...]
[@装饰器2]
[@装饰器1]
def 函数名([位置形参], [*元组形参], [命名关键字形参], [**字典形参]):
"文档字符串"
语句块
2.函数的定义def
语法概述:
def 函数名(参数列表):
语句块(代码块)
语法说明:
1.函数名就是语句块的名称是变量,它在创建时绑定一个函数,命名规则与变量名命名规则相同必须为标识符
2.函数名是一个变量不要轻易对其赋值,函数的自己的命字空间,在函数内部可以访问外部的变量,但外部的语句不能访问函数内部的变量
3.函数内部有独立的运行空间,函数外部不能访问函数内的变量;函数如果不需要传入参数,则参数列表可以为空
4.语句部分不能为空,如果为空需要填充pass语句;没有返回值叫过程有返回值叫函数,没有返回值的函数默认返回None
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def say_hello(): print("hello world!") print("hello coco!")
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def number_int(num): print('在函数内部 %d 对应的内存地址是 %d' % (num, id(num))) # pass # 1> 定义一个字符串变量 result = "hello" print("函数要返回数据的内存地址是 %d" % id(result)) # 2> 将字符串变量返回,返回的数据的引用而不是数据本身 return result # 定义一个数字变量 a = 10 # 数据的地址本质上就是一个数字 print("a变量保存的数据内存地址是 %d" % id(a)) # 1.调用函数时,本质上传递的是实参保存的数据引用而不是实参保存的数据 # 2.如果函数有返回值但没有定义变量接收程序不会报错但是无法获得返回结果 r_int = number_int(a) print("%s 的内存地址是 %d" % (r_int, id(r_int)))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def f1(lst=[]): print("f1函数被调用") f1() f1 = None f1() # 出错,f1 绑定的是None
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def f1(): print("hello") def f2(): print("world") f1, f2 = f2, f1 f1() # world
3.函数的调用
语法概述: 函数名(实际调用传递参数) # 实际调用传递参数简称"实参"
语法说明: 函数调用是一个表达式,如果函数需要返回其它的对象则需要用到return语句;如果没有return语句,函数执行完毕后返回None对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def print_line(char, times): """打印单行分割线 :param char: 分割线使用的分割字符 :param times: 分割线重复的次数 """ print(char * times) def print_lines(char=input("要打印的分割线标记:"), times=int(input("每行打印分割线的标记数:"))): """打印指定行数的分割线 :param char: 指定分割线的分割字符 :param times: 指定分割线的重复次数 """ i = 0 # 指定分割线的打印行数 j = int(input("输入分割线行数: ")) while i < j: print_line(char, times) i += 1 print_lines()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 函数的前向引用 name = "zhangsan" # 第1步 def name1(): # 第2步 name = "lisi" # 第4.1步 def lisi(): # 第4.2步 nonlocal name # 第4.3.1步 nonlocal指定上一级的变量 name = "wangwu" # 第4.3.2步 lisi() # 第4.3步 print(name) # 第4.4步 1.打印wangwu name2() # 第4.5步 def name2(): # 第3步 print(name) # 第4.5.1步 2.打印zhangsan name1() # 第4步 print(name) # 第5步 3.打印zhangsan
4.函数的返回return
语法概述: return [表达式] # []代表其中的内容可以省略
语法说明:
1.return语句后跟的表达式可以省略,省略后相当于return None
2.如果函数内没有return语句,则函数执行完最后一条语句后返回None相当于在最后加了一条return None语句
3.return用于函数中结束当前函数的执行,返回到调用函数的地方同时返回一个对象的引用关系
4.函数可以返回另一个函数也可以做为另一个函数的返回值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 带参函数定义和调用及return返回值 def add(a, b): return a + b ret = add(1,2) print(ret) # 3
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def sum_2_num(num1,num2): """对两个数字的求和""" result = num1 + num2 # 可以使用返回值,告诉调用函数一方的计算结果 return result # 注意: return 就表示返回,下方的代码不会被执行到 # 可以使用变量,来接收函数执行的返回结果 sum_result = sum_2_num(10,20) print("计算结果是: %d" % sum_result)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def measure(): """测量温度和湿度""" print("测量开始") temp = 39 wetness = 50 print("测量结束") # 如果函数返回的类型是元组,小括号可以省略,元组可以一次返回多个值 # return (temp, wetness) return temp, wetness result = measure() print(result) # 需要单独处理温度或湿度 print(result[0]) print(result[1]) # 如果函数的返回类型是元组,同时希望单独处理元组中的元素,可以使用多个变量,一次接收函数的返回结果 # 使用多个变量接收结果时,变量的个数应该和元组中元素的个数保持一致 gl_temp, gl_wetness = measure() print(gl_temp) print(gl_wetness)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def get_op(): s = input("请输入您的操作: ") if s == "求最大": return max elif s == '求最小': return min elif s == '求和': return sum L = [2,4,6,8,10] print(L) f = get_op() print(f(L))
5.函数的嵌套
# def语句的作用是用来创建一个函数 def get_func(value): if value == 1: # def语句可以写在函数内部,在函数执行时可以动态创建一个函数 def myadd(x, y): return x + y return myadd elif value == 2: def mysub(x, y): return x - y return mysub fx = get_func(1) print(fx(400, 300)) # 700 fx = get_func(2) print(fx(400, 300)) # 100
6.函数的参数
1.不可变和可变的参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(num, num_list): # 形参:被调用时分配内存,调用结束后释放,函数内部有效,函数内部作为变量使用 print("函数内部的代码") num = 100 # 在函数内部针对形参使用赋值语句,仅会修改内部形参变量,不会修改外部实参变量 num_list = [1, 2, 3] print(num, num_list) # 100 [1, 2, 3] print("函数执行完成") gl_num = 10 gl_list = [4, 5, 6] demo(gl_num, gl_list) # 实参 print(gl_num, gl_list) # 10 [4, 5, 6]
2.函数内部通过方法修改可变参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(num_list): print("函数内部的代码") # 使用方法修改列表的内容,会影响外部的数据 num_list.append(9) print(num_list) print("函数执行完成") gl_list = [1, 2] demo(gl_list) print(gl_list)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(num, num_list): print("函数开始") # num = num + num num += num # num_list = num_list + num_list # 列表变量使用+不会做相加再赋值的操作! num_list += num_list # +=本质上是在调用列表的 extend 方法 # num_list.extend(num_list) print(num, num_list) print("函数执行完成") gl_num = 9 gl_list = [1, 2] demo(gl_num, gl_list) print(gl_num, gl_list)
3.一个函数可以作为另一个函数的实参参数传递
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def f1(): print("f1函数被调用") def f2(): print("f2函数被调用") def fx(fn): print("fn绑定的函数是:", fn) # 在fx内调用fn绑定的函数 fn() fx(f1) # 调用fx,把f1作为实参传数 fx(f2) # 调用fx,把f2作为实参传数 """执行结果 fn绑定的函数是: <function f1 at 0x0000000004719C80> f1函数被调用 fn绑定的函数是: <function f2 at 0x0000000004719BF8> f2函数被调用 """
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def goodbye(L): for x in L: print("再见:", x, end=' ') def hello(L): for x in L: print("欢迎:", x, end=' ') def fx(fn, L): print("fx被调用", end=' ') fn(L) fx(hello, ['Coco', 'Angels']) # fx被调用 欢迎: Coco 欢迎: Angels fx(goodbye, ['可可', '天使']) # fx被调用 再见: 可可 再见: 天使
4.函数的参数传递--实参
1.位置传参: 实际调用参数(实参)的对应关系与形式参数(形参)的对应关系是按位置来依次对应的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fx(a, b, c): pass # ^ ^ ^ fx( 1, 2, 3)
2.序列传参(动态传传递位置参数): 在函数调用过程中,用*将序列拆解后按位置进行传递的传参方式,实参和形参通过序列传递和匹配
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fx(a, b, c): pass s1 = [11, 22, 33] # 列表 fx(*s1) # 将s1序列拆解后按位置传入fx中
3.关键字传参: 传参时按着形参的名称给形参赋值,即实参和形参按名称进行匹配,传参时可以不按位置进行匹配
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fx(a, b, c): pass fx(b=22, c=33, a=11) # 11->a, 22->b, 33->c
4.字典关键字传参(动态传递关键字参数): 用**拆解字典后再进行关键字传参,字典的键名和形参名必须一致,键名为字符串并且要在形参中存在
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fx(a, b, c): pass d = {'c': 33, 'b': 22, 'a': 11} fx(**d) # 拆解字内再依次按关键字传参
5.综合传参: 函数的传参方式在能确定形参能唯一匹配到相应实参的情况下可以任意组合
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fx(a, b, c, d, e, f): pass fx(10, *[20, 30], e=50, **{'d':40, 'f':60}) # 通常位置传参和序列传参先传递,其次是关键字传参和字典关键字传参 fx(e=50, **{'d':40, 'f':60}, 10, *[20, 30]) # 错误的做法
5.函数的参数接收--形参
形参概述: 缺省参数,位置形参,星号元组形参,命名关键字形参,双星号字典形参可以混合使用
1.函数的缺省参数
语法概述:
def 函数名(形参名1=默认实参1, 形参名2=默认实参2, ...): pass
缺省参数必须自右至左依次存在,缺省参数可以有0个,1个,多个,甚至全部都有缺省参数
缺省参数的绑定对象存在于函数内,同函数的生命周期一致
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def info(name, age=1, address="不详"): print("我叫", name, '我今年:', age, '岁, 家庭住址:', address) info("Coco", 30, "中国") info("Angels", 18) info("Cat")
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 缺省参数的绑定对象存在于函数内,同函数的生命周期一致 def fun(a, lst=[]): lst.append(a) print(lst) l = [1, 2, 3] fun(4, l) # [1, 2, 3, 4] print(l) # [1, 2, 3, 4] fun(5, l) # [1, 2, 3, 4, 5] print(l) # [1, 2, 3, 4, 5] fun(6) # [6] fun(7) # [6, 7] print(l) # [1, 2, 3, 4, 5]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def print_info(name, gender=True): """ :param name: 班上同学的姓名 :param gender:True 男生 False 女生 """ gender_test = "男生" if not gender: gender_test = "女生" print("%s 是 %s" % (name, gender_test)) # 在指定缺省参数的默认值时,应该使用最常见的值作为默认值 print_info("小明") print_info("小美", False)
2.函数的形参定义方式
1.位置形参
def 函数名(形参名1, 形参名2, ....): pass
2.星号元组形参(动态接收位置参数)
def 函数名(*元组形参名): pass # 收集多余的位置传参,元祖形参名通常叫 args
3.命名关键字形参
# 所有的命名关键字形参都强制调用者采用关键字传参或字典关键字传参的方式传递
def 函数名(*, 命名关键字形参): pass
def 函数名(*args, 命名关键字形参): pass
4.双星号字典形参(动态接收关键字参数)
def 函数名(**字典形参名): pass # 收集多余的关键字传参,字典形参名通常叫 kwargs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def myfun2(a, b, *args, c, **kwargs): print(a, b, args, c, kwargs) # 10 20 (30, 40, 'a', 'b') 50 {'d': 60} myfun2(10, 20, 30, 40, *'ab',**{'d': 60}, c=50)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# *作为标识符存在,标识着*右面的所有参数都是关键字参数 # *args,*后面加元组形参名,表示可以接收多个位置参数 def myfun(a, *, k): print('a=', a, 'k=', k) # k强制使用关键字传参 myfun(10, k=20) # a= 10 k= 20 # 字典关键字传参 myfun(100, **{'k':200}) # a= 100 k= 200
3.函数形参接收顺序
函数参数自左至右的顺序为: 位置形参-->星号元组形参-->命名关键字形参-->双星号字典形参
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fn(a, b, *args, c, **kwargs): print(a, b, args, c, kwargs) # 100 200 (300, 400, 'A', 'B') 100 {'d': 'D'} fn(100, 200, 300, 400,*"AB", **{'d':"D"}, c=100) # 100 200 (300, 400, 'A', 'B') C {'d': 'D', 'e': 100} fn(100, 200, 300, 400,*"AB", **{'d':"D", "c":"C"}, e=100)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 可以接收任意位置传参和关键字传参的函数 def fn(*args, **kwargs): pass
7.函数的文档字符串
1.语法概述:
def 函数名(参数列表):
"""函数的文档字符串"""
函数语句块
2.语法说明:
1.文档字符串通常来用说明本函数的功能和使用方法;在交互模式下,输入help(函数名)可以查看函数的"文档字符串"
2.插入多行文档字符串方法是在pycharm中点击函数名或者def定义语句选则: Insert documentation string stub
3.函数的__doc__属性:
函数内第一次末赋值给任何变量的字符串是此函数的文档字符串,此字符串会自动赋值给函数的__doc__属性
示例:
def func():
"""这是一个文档字符串"""
pass
print(func.__doc__) # 这是一个文档字符串
8.内置获取属性函数dir
语法概述: dir([对象]) # 返回一个字符串列表
语法说明:
1.如果没有参数调用则返回当前作用域内的所有变量的列表,如果给定一个对象作为参数则返回这个对象的所有变量的列表
2.对于模块,返回这个模块的全部属性
3.对于一个类对象,返回类对象的所有变量,并递归基类对象的所有属性;对于其它对象返回所有变量,类变量和基类变量
9.内置获取变量函数 globals, locals 与声明语句 global, nonlocal
globals() # 返回全局作用内变量的字典
locals() # 返回局部作用域内变量的字典
global
语法概述: global 变量名
语法说明:
1.global语句声明的一个或多个变量,这些变量的作用域为模块级的作用域的变量,即全局变量
2.global全局声明将赋值变量映射到模块文件内部的作用域,global变量列表里的变量名不能出现在此作用域内的形参列表里
3.全局变量在函数内部不经过声明就可以直接访问,但是在函数内部被赋值,则必须经过全局声明否则会被认为是局部变量
4.不能先声明局部变量,再用global声明为全局变量,此做法不附合规则
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 全局变量不能出现在此作用域内的形参列表里 def fun1(a): global a """会报错 File "<ipython-input-6-d92ed22bfd1d>", line 5 SyntaxError: name 'a' is parameter and global """
nonlocal
语法概述: nonlocal 变量名
语法说明:
1.nonloca声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量
2.nonlocal语句只能在被嵌套函数内部进行使用,nonlocal语句的变量列表的变量名不能出现在此函数的参数列表里
3.访问nonlocal变量将对外部嵌套函数作用域内的变量进行操作
4.当有两层或两层以上函数嵌套时,访问nonlocal变量只对最近一层的变量进行操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def f1(): v =100 def f2(): v = 200 def f3(): nonlocal v v += 1 # 此时只对f2的v进行操作 f3() f2()
10.内置执行函数eval和exec
eval
语法概述: eval(source, globals=None, locals=None)
语法说明: 把一个字符串当成一个表达式执行,返回表达式执行后的结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
x = 100 y = 200 s = "x+y" v = eval(s) print(a) print(eval("x+y", {'x':10, 'y':20})) # 30 print(eval("x+y", {'x':10, 'y':20}, {'x':1, 'y':2})) # 3 print(eval("x+y", {'x':10, 'y':20}, {'x':1})) # 21
eval函数的危险操作
# 在开发是不要直接使用eval直接转换input输入的结果
print(eval(input("请输入:"))) # 执行成功返回0, 执行失败返回错误信息
当输入"__import__('os').system('ls')"时可以列表出当前目录
当输入"__import__('os').system('rm *')"时可以删除当前目录下的内容
exec
语法概述: exec(source, globals=None, local=None)
语法说明: 把一个字符串当成程序来执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
s = 'x=100; print("hello"); x += 1; print(x)' print(s) exec(s) 执行结果: x=100; print("hello"); x += 1; print(x) hello 101
11.内置迭代工具函数zip和enumerate
zip和enumerate都是用来生成一个个性化的可迭代对象
zip
语法概述: zip(iter1[, iter2, ...])
语法说明: 返回一个用于生成一个元组的zip对象,元组的个数是由最小的可迭代对象决定,元组内容是可迭代对象iter1和iter2中元素的组合
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
print(list(zip(("a", "b", "c"), (1, 2, 3)))) # [('a', 1), ('b', 2), ('c', 3)] # max和zip高级连用 age = {"age": 10, "age1": 20, "age2": 30, "age3": 12, } print(max(zip(age.values(), age.keys()))) # [30, 'age2']
enumerate
语法概述: enumerate(iterable,start)
语法说明: 生成带索引的枚举对象,返回的迭代类型为索引-键值对(index-value),默认索引从零开始,也可以用s
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
dic = {"name": "zhangsan", "age": 18} for item in enumerate(dic, 1): print(item, end=' ') # (1, 'name') (2, 'age')
12.函数式编程
概述: 函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量;任意一个函数,
只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用;而允许使用变量的程序设计语言,由
于函数内部的变量状态不确定,同样的输入可能得到不同的输出,因此这种函数是有副作用的;函数式编程的一
个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数;Python对函数式编程提供部分支
持,由于Python允许使用变量,因此Python不是纯函数式编程语言
定义:
1.是指用一系列函数解决问题
2.不可变: 不用变量保存状态,不修改变量
3.函数即变量: 函数名可以当参数传递,返回值可以是函数名
4.尾调用: 在函数的最后一步调用另外一个函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 非函数式 a = 1 def funck(): global a a += 1 return a print(funck()) # 函数式 b = 1 def funck1(b): return b+1 print(funck1(b))
函数本身优势:
1.函数本身是对象,可以赋值给变量,赋值后变量绑定函数
2.允许将函数作为实参传入另一个函数,允许函数返回一个函数
3.函数是一等公民,优先使用函数解决问题
函数式编程的优势:
1.每一个函数完成细小的功能,一系列函数的任意组合可以解决大的问题
2.函数仅接收输入并产生输出,不会影响其它全局变量的状态
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 求 1+ 2 + 3 + ...... + n 的和 print(sum(range(n+1)))
13.匿名函数lambda表达式
语法概述: lambda 形参1, 形参2, 形参n: 表达式 # 创建一个匿名函数对象,同def类似但不提供函数名
语法说明:
1.lambda 只是一个表达式,它用来创建一个函数对象
2.lambda 表达式调用时,先执行冒号后的表达式,并返回表达式的结果的引用
3.lambda 表达式创建的函数只能包含一条表达式
4.lambda 比函数简单,且可以随时创建和销毁,有利于减少程序的偶合度
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def myadd(x, y): return x + y # 可以改写为 myadd = lambda x, y: x + y print(myadd(100, 200)) # 300 print(myadd("ABC", "100")) # ABC100
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
fx = lambda x: (x**2 + 1)%5 == 0 print(fx(4)) # False print(fx(3)) # True mymax = lambda x, y: x if x > y else y print(mymax(55, 63)) # 63
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fx(f, x, y): r = f(x, y) print(r) fx((lambda a, b: a + b), 100, 200) # 300 fx((lambda x, y: x ** y), 3, 4) # 81
14.递归函数recursion
1.语法概述
1.递归必须有一个明确的结束条件,几乎所有的递归都能用循环来代替,递归是函数直接或间接的调用自身,使用时优先考虑尾递归
2.每次进入更深一层递归时,问题规模相比上一次递归应有所减少,递归效率不高,递归层数过多会导致栈(stack)溢出
3.每进入一个函数调用就会增加一层栈帧,每当函数返回,就会减少一层栈帧,栈的大小不是无限的,所以递归次数过多会导致栈溢出
4.递归的优缺点和实现技巧
优点: 递归可以把问题简单化,让路径更为清晰,代码更为简洁
缺点: 递归因系统环境影响大,当递归深度太大时,可以会得到不可预知的结果,递归效率比for循环低
技巧: 写递归函数技巧就是先假设函数已经实现
5.递归函数流程图: https://www.processon.com/view/link/5ec318b0e0b34d5f261723c3
2.非尾递归
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def cal(seq): print(seq) if len(seq) == 1: return seq[0] first, second, *args = seq seq[0] = first + second seq.pop(1) return cal(seq) x = cal([i for i in range(100)]) print(x)
3.尾递归: 上一层函数已经结束,所以不保留上一层栈帧,优化程序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def cal(seq): print(seq) if len(seq) == 1: return seq[0] first, second, *args = seq seq[0] = first + second seq.pop(1) return cal(seq) x = cal([i for i in range(100)]) print(x)
4.递归深度-->默认997
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import sys sys.setrecursionlimit(1500) # 可以调整递归深度,但是不一定能跑到 def func(n): print("递归深度%s" % n) func(n+1) func(1)
5.递归函数的实现方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 求:100 + 99 + 98 + 97 + ..... + 1 的和 # 分析: 先假设 mysum(x) 已经完成,且能求x + (x-1) + .. +1的和 def mysum(x): # 选判断终止条件 if x == 1: return 1 return x + mysum(x - 1) print(mysum(100)) # 5050
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 运用函数的尾递归计算[1, 100]中能被3和7整除的数的个数和求和 def func(start, end, a=0, b=0): if start > end: return a, b if start % 3 == 0 and start % 7 == 0: a += 1 b += start ret = func(start+1, end, a, b) return ret res = func(1, 100) print("1到100中能被3和7整除的数的个数%s个," "满足要求的数的求和是%s" % (res[0], res[1]))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 借钱真难 import time person_list = ["张三", "李四", "王五", "coco"] def borrow_money(preson_list): print("=" * 60) if len(person_list) == 0: return "没人愿意借给你" # 递归出口1 person = person_list.pop(0) if person == "coco": return "%s让你去找他,他借给你" % person # 递归出口2 print("%s有钱借我100元吗?" % person) print("%s回答道: 我没有钱,我帮你问问%s" % (person, person_list[0])) time.sleep(2) res = borrow_money(person_list) print("%s问的结果是: %s" % (person, res)) time.sleep(2) return res borrow_money(person_list)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 递归遍历目录树形结构 import os file_path = r"G:\PycharmProjects\01_Python基础" def read(file_path, n): files = os.listdir(file_path) # 获取当前文件夹中的所有文件 for file in files: # 遍历文件夹中的本层文件名 file_d = os.path.join(file_path, file) # 取当前的绝对路径 if os.path.isdir(file_d): # 判断是否是文件夹 # 打印带递归深度的文件夹,递归深度代表着缩进程度 print('\t'*n, file) read(file_d, n+1) # 是文件夹则递归遍历 else: # 打印带递归深度的文件,递归深度代表着缩进程度 print("\t"*n, file) # 递归出口 pass read(file_path, 0)
6.递归函数与for循环效率对比
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 斐波那契数列:1, 2, 3, 5, 8, 13, 21.....根据这样的规律,计算30个斐波拉契数 import time #递归 def fib(num): if num == 1: return 1 elif num == 2: return 2 return fib(num - 1) + fib(num - 2) # for循环 def fib2(num): n1 = 1 n2 = 2 if num == 1: return 1 elif num == 2: return 2 else: for i in range(3,num + 1):#range(3,3) n1 , n2 = n2 , n1 + n2 return n2 front_time = time.time() fib2(30) last_time = time.time() print('fib2===>for循环耗时: ',last_time - front_time) # for循环耗时: 0.00819087028503418 front_time = time.time() fib(30) last_time = time.time() print('fib===>递归耗时',last_time - front_time) # fib===>递归耗时 0.2355349063873291
15.高阶函数概述与常用的内置高阶函数(map,filter,sorted,reduce)
高阶函数概述: 满足下列条件中的一个的函数即为高阶函数
1.一个函数的形参可以接收另一个或多个函数作为参数
2.一个函数的返回值为另外一个函数,若返回值为该函数本身则为递归
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def test11(): # 是高阶函数,返回值包含函数 print("11") return test22(test11) def test22(n): # 是高阶函数,参数是函数 print(n) def test33(): # 是高阶函数,返回值是函数 print("33") return test11() test22(test33())
映射高阶函数 map
语法概述: map(func, *iterables) # 参数一执行方法,参数二为一个或多个可迭代对象
语法说明:
1.用函数对可迭代对象中的每一个元素作为参数计算出新的可迭代对象(可以使用一次的内存地址)
2.当最短的一个可迭代对象不再提供数据时,此可迭代对象生成结束,得到的这个迭代器列表的元素个数和位置与原来一样
3.map函数是一个生成器函数,返回一个生成器对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成一个可迭代对象,要求此可迭代对象可以生成1~9自然数的平方 1, 4, 9, 16, .... 81 def power2(x): return x**2 for x in map(power2, range(1, 10)): print(x) # 求数据1 + 4 + 9 + 16 + ... + 81的和 print(sum(map(power2, range(1, 10))))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成一个可迭代对象,要求此可迭代对象生成 # 1**4, 2**3, 3**2, 4**1 # 1 8 9 4 for x in map(pow, [1,2,3,4], [4,3,2,1]): print(x)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 模拟map函数实现 num_1 = [1, 3, 5, 7, 9] def map_test(func, array): ret = [] for i in array: res = func(i) ret.append(res) return ret print(map_test(lambda x:x+1, num_1))
过滤高阶函数 filter
语法概述: filter(func, iterable) # 参数一为判断函数,参数二为一个可迭代对象
语法说明:
1.筛选可迭代对象iterable中的数据,返回一个可迭代对象,此可迭代对象将对iterable进行筛选
2.函数将对iterable中的每个元素进行求值,返回False时将此数据丢弃,返回True,则保留此数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# isodd函数判断x是否为奇数,是奇数返回True def isodd(x): return x % 2 == 1 # 打印10以内的奇数: for x in filter(isodd, range(10)): print(x) # 生成10以内所有偶数的列表,用filter实现 L = [x for x in filter(lambda x: x%2==0, range(10))]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 模拟 filter 函数实现 mv_list = ["阮小二", "阮小五", "阮小七", "武大郎"] def test1(func, array): ret = [] for i in array: if not func(i): ret.append(i) return ret res = test1(lambda n:n.endswith("阮"), mv_list) print(res)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# map 返回执行后的结果值-->得到结果 print(list(map(lambda x: x%2==0, range(4)))) # [True, False, True, False] # filter 返回满足条件的结果值-->清洗数据 print(list(filter(lambda x: x%2==0, range(4)))) # [0, 2]
排序高阶函数 sorted
语法概述: sorted(iterable, key=None, reverse=False) # 将原可迭代对象的数据进行排序,生成排序后的列表
语法说明:
iterable 可迭代对象
key 绑定函数,此函数用来提供一个排序的依据
reverse 排序规则: reverse = True 降序; reverse = False 升序默认
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
L = [5, -2, -4, 0, 3, 1] L2 = sorted(L) # L2 = [-4, -2, 0, 1, 3, 5] L3 = sorted(L, reverse=True) # L3 = [5, 3, 1, 0, -2, -4]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
L = [5, -2, -4, 0, 3, 1] # 依据: abs(5), abs(-2), abs(-4), abs(0), abs(3), abs(1) L4 = sorted(L, key=abs) # L4 =[0, 1, -2, 3, -4, 5]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
names = ['Tom', 'Jerry', 'Spike', 'Tyke'] L = sorted(names) # L = ['Jerry', 'Spike', 'Tom', 'Tyke']
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 根据名字的长度进行排序 L2 = sorted(names, key=len) # L2 = ['Tom', 'Tyke', 'Jerry', 'Spike']
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 自定义排序规则 def fr(s): return s[::-1] # 把name中的每一个字符串翻转 L3 = sorted(names, key=fr) # L3 = ['Spike', 'Tyke', 'Tom', 'Jerry']
迭代高阶函数 reduce
语法概述: reduce(function, iterable[, initializer]) # function函数,iterable可迭代对象,initializer可选初始参数
语法说明:
1.reduce函数会对参数序列中元素进行累积
2.reduce函数先将可迭代对象传递给function函数操作,得到的结果与第三个参数再传参给function函数操作,返回得到的最终结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from functools import reduce num1 = [1, 2, 3, 4, 5] def my_reduce(func, array, init=None): """模拟reduce函数原理""" if init is None: res = array.pop(0) else: res = init for i in array: res = func(res, i) return res msg = my_reduce(lambda i,j: i + j, num1, 10) print(msg) # 25 # reduce函数遍历合并可迭代对象中的每个元素 msg = reduce(lambda i, j: i + j, num1, 10) print(msg) # 25
16.闭包closure
闭包概述
1.将内嵌函数的语句和这些语句的执行环境打包在一起后,得到的函数对象称为闭包,作用就是让一个变量量能够常驻内存供后面的程序使用
2.如果一个内嵌函数访问了外部嵌套函数作用域内的变量,并且该内嵌函数可以在其定义环境外被执行,则这个内嵌函数就是闭包
3.闭包流程图: https://www.processon.com/view/link/5edb93f50791297145d51f8d
4.闭包必须满足以下三个条件:
1.必须有一个内嵌函数
2.内嵌函数必须引用外部函数中的变量
3.内嵌函数可以在其定义环境外被执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 闭包:变量常驻内存,防止其他程序修改 def func(): name = "coco" # 常驻内存,func执行完毕后不会被回收 def func1(): # 在内层函数中调用了外层函数的变量叫闭包,可以让一个局部变量常驻内存 print(name) print(func1.__closure__) # 返回一个cell的内存地址则是闭包,否则返回None return func1 ret = func() # 如果没有闭包,func执行完成后name变量会被回收 ret() # 执行func1()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def make_power(y): def fn(x): return x ** y return fn pow2 = make_power(2) # pow2 = fn(x) 携带变量y = 2 print("5的平方是: ", pow2(5)) # 25
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 闭包的常见运用: 爬虫 from urllib.request import urlopen def but(): content = urlopen("http://www.xiaohua100.cn/index.html").read() def get_content(): # 在函数内部使用了外部的变量 return content return get_content fn = but() # 这个时候就开始加载校花100的内容 # 后⾯面需要⽤用到这里面的内容就不需要在执行非常耗时的网络连接操作了 content = fn() # 获取内容 print(content) content2 = fn() # 重新获取内容 print(content2)
17.装饰器decorators
1.装饰器概述:
1.装饰器是一个函数,这个函数的主要作用是包装别一个函数或类,包装的目的是在不改变原函数名的情况下改变被包装对象的行为
2.函数装饰器是指装饰器是一个函数,传入的是一个函数,返回的也是一个函数;装饰器 = 高阶函数 + 函数嵌套 + 闭包
3.装饰器本质就是函数是为其他函数添加附加功能,装饰器是在被解释器自上而下解释时已经执行,而非调用时执行
4.装饰器原则: 不修改被修饰函数的源代码和调用方式
5.多个装饰器装饰函数是自下而上依次装饰的,所有被装饰的函数调用时会最先体现最外层装饰器的功能
@装饰器3 # 解释器最后装饰,被装饰的函数调用时最先执行其装饰的功能
@装饰器2 # 解释器其次装饰,被装饰的函数调用时其次执行其装饰的功能
@装饰器1 # 解释器最先装饰,被装饰的函数调用时最后执行其装饰的功能
def fn():
pass
2.装饰器原理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def mydeco(fn): # 装饰器函数 def fx(): print("开始装饰hello函数") fn() print("结束装饰hello函数") return fx def hello(): # 被装饰的函数 print("hello") # 此时将hello变量绑定在了mydeco返回的函数上 hello = mydeco(hello) hello() """执行结果 开始装饰hello函数 hello 结束装饰hello函数 """
3.对一个没有参数和返回值的函数进行装饰
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def set_func(func): def call_func(): print("权限验证1") print("权限验证2") func() return call_func @set_func # 等价于test1 = set_func(test1) def test1(): print("this is test1 function") # ret = set_func(test1) # ret() test1() """执行结果 权限验证1 权限验证2 this is test1 function """
4.对有参数但没有返回值的函数进行装饰
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def set_func(func): def call_func(num): print("权限验证1") print("权限验证2") func(num) return call_func @set_func # 等价于test1 = set_func(test1) def test1(num): print("this is test1 function---%s" % num) test1(100) """执行结果 权限验证1 权限验证2 this is test1 function---100 """
5.一个装饰器装饰多个函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def set_func(func): def call_func(num): print("权限验证1") print("权限验证2") func(num) return call_func @set_func # 等价于test1 = set_func(test1) def test1(num): print("this is test1 function---%s" % num) @set_func # 等价于test2 = set_func(test2) def test2(num): print("this is test2 function---%s" % num) test1(100) test1(200) """执行结果 权限验证1 权限验证2 this is test1 function---100 权限验证1 权限验证2 this is test2 function---100 """
6.验证装饰器在调用函数之前已经被装饰
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def set_func(func): print("开始进行对函数%s的装饰" % func.__name__) def call_func(num): print("权限验证1") print("权限验证2") func(num) print("确认完成对函数%s的装饰" % func.__name__) return call_func @set_func # 等价于test1 = set_func(test1) def test1(num): print("this is test1 function---%s" % num) @set_func # 等价于test2 = set_func(test2) def test2(num): print("this is test2 function---%s" % num) """执行结果 开始进行对函数test1的装饰 确认完成对函数test1的装饰 开始进行对函数test2的装饰 确认完成对函数test2的装饰 """
7.验证多个装饰器加载顺序和调用函数执行顺序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fun1(fn): print("解释器加载时,装饰器func1开始装饰") def fx(): print("调用fun函数时,装饰器fun1开始装饰") fn() print("结束调用fun函数时,装饰器fun1结束装饰") return fx def fun2(fn): print("解释器加载时,装饰器func2开始装饰") def fx(): print("调用fun函数时,装饰器fun2开始装饰") fn() print("结束调用fun函数时,装饰器fun2结束装饰") return fx def fun3(fn): print("解释器加载时,装饰器func3开始装饰") def fx(): print("调用fun函数时,装饰器fun3开始装饰") fn() print("结束调用fun函数时,装饰器fun3结束装饰") return fx @fun3 @fun2 @fun1 def fun(): print("被装饰的函数fun") fun() """执行结果 解释器加载时,装饰器func1开始装饰 解释器加载时,装饰器func2开始装饰 解释器加载时,装饰器func3开始装饰 调用fun函数时,装饰器fun3开始装饰 调用fun函数时,装饰器fun2开始装饰 调用fun函数时,装饰器fun1开始装饰 被装饰的函数fun 结束调用fun函数时,装饰器fun1结束装饰 结束调用fun函数时,装饰器fun2结束装饰 结束调用fun函数时,装饰器fun3结束装饰 """
8.装饰器的完整语法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def timmer(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print("函数的运行时间是%s" % (stop_time - start_time)) return res return wrapper @timmer # @timmer-->cal = timmer(cal) def cal(li): res = 0 for i in li: time.sleep(0.1) res += 1 return res res = cal(range(20)) # 就是在运行timmer的返回值wrapper print(res) """执行结果 函数的运行时间是2.0593690872192383 20 """
9.带有参数的装饰器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def set_level(level_num): def set_func(func): def call_func(*args, **kwargs): if level_num == 10: print("权限验证1") elif level_num == 20: print("权限验证2") return func(*args, **kwargs) return call_func return set_func @set_level(10) # 等价于test1 = set_func(test1) def test1(num): print("this is test1 function---%s" % num) @set_level(20) # 等价于test2 = set_func(test2) def test2(num): print("this is test2 function---%s" % num) test1(100) test2(200) """执行结果 权限验证1 this is test1 function---100 权限验证2 this is test2 function---200 """
18.迭代器Iterator
1.迭代器概述
1.迭代器是访问可迭代对象的一种方式,用迭代器可以访问可迭代对象
2.迭代器是指iter(可迭代对象)返回的对象,可以用next(it)函数获取可迭代对象的数据
3.迭代器只能往前取值不会后退,用iter函数可以返回一个可迭代对象的迭代器
4.迭代器特点: 节省内存, 惰性机制(不到最后不拿值), 只前进不后退
2.迭代器函数iter和next
iter(iterable): 从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象
next(iterator): 从迭代器iterator中获取下一个记录,如果无法获取下一条记录,则触发StopIterator异常
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
L = [2, 3, 5, 7] # 确定L列表是可迭代对象 print("__iter__" in dir(L)) # True # 确定L列表不是迭代器 print("__next__" in dir(L)) # False it = iter(L) # 用iter返回一个迭代器用it绑定 # 确定it对象是可迭代对象 print("__iter__" in dir(it)) # True # 确定it对象是迭代器 print("__next__" in dir(it)) # True # 用next(it)用迭代器来获取L中的元素 next(it) # 2 next(it) # 3 next(it) # 5 next(it) # 7 next(it) # StopIteration #通知next调用者,已无数据 # 用迭代器获取 range对象的数据 it = iter(range(1, 10, 3)) next(it) # 1 next(it) # 4 next(it) # 7 next(it) # StopIteration
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
list1 = [1, 2, 3, 4] for i in list1: # 本质在调用列表调内置的__iter__方法把列表转换成可迭代对象再调用__next__ print(i) list2 = list1.__iter__() # # 遵循迭代器协议生成可迭代对象 print(list2.__next__()) print(list2.__next__()) print(list2.__next__()) print(next(list2)) # next--->就是在调用list1.__next__()取迭代器的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 使用while循环+迭代器来模拟for循环,for循环的本质就是用迭代器去拿可迭代对象的数据 lst = [1,2,3] lst_iter = lst.__iter__() while True: try: i = lst_iter.__next__() print(i) except StopIteration: break
19.迭代器原理剖析
迭代器协议:
1.迭代器议是指对象能够使用next函数获取下一项数据, 在没有下一项数据时触发一个StopIteration异常来终止迭代的约定
2.迭代器协议的实现方法: 在类内需要定义 __next__(self) 方法来实现迭代器协议
3.迭代器原理剖析流程图: https://www.processon.com/view/link/5ee1bbcde401fd1fd2886be9
4.语法形式:
class MyIterator:
def __next__(self):
迭代器协议
return 数据
可迭代对象概述:
1.可迭代对象是指能用 iter(obj) 函数返回迭代器的实例对象
2.可迭代对象的内部要定义 __iter__(self) 方法来返回迭代器对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# for 循环内部迭代器原理剖析 import time from collections import Iterable from collections import Iterator class Classmate(object): def __init__(self): self.names = list() self.current_num = 0 def add(self, name): self.names.append(name) # 如果想要一个对象称为一个可迭代对象,即可以使用for,那么必须实现__iter__方法 def __iter__(self): return self def __next__(self): if self.current_num < len(self.names): # 避免超出索引下标 ret = self.names[self.current_num] self.current_num += 1 return ret else: raise StopIteration # 取完值后主动抛出异常停止迭代 classmate = Classmate() classmate.add("张三") classmate.add("李四") classmate.add("王五") print("判断classmate是否是可以迭代的对象: ", isinstance(classmate, Iterable)) classmate_iterator = iter(classmate) print("判断classmate_iterator是否是迭代器: ", isinstance(classmate_iterator, Iterator)) print(next(classmate_iterator)) for name in classmate: print(name) time.sleep(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 迭代器实现fibonacci数列-->存储生产数列的方法,节省存储资源 class FibIterator(object): """斐波那契数列迭代器""" def __init__(self, n): """ :param n: int, 指明生成数列的前n个数 """ self.n = n # current用来保存当前生成到数列中的第几个数了 self.current = 0 # num1用来保存前前一个数,初始值为数列中的第一个数0 self.num1 = 0 # num2用来保存前一个数,初始值为数列中的第二个数1 self.num2 = 1 def __next__(self): """被next()函数调用来获取下一个数""" if self.current < self.n: num = self.num1 self.num1, self.num2 = self.num2, self.num1 + self.num2 self.current += 1 return num else: raise StopIteration def __iter__(self): """迭代器的__iter__返回自身即可""" return self if __name__ == "__main__": fib = FibIterator(10) for num in fib: print(num, end=" ") # 0 1 1 2 3 5 8 13 21 34 # 除了for循环能接收可迭代对象,list,tuple等也能接收 print() # 打印一个换行 li = list(FibIterator(15)) print(li) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377] tp = tuple(FibIterator(6)) print(tp) # (0, 1, 1, 2, 3, 5)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class MyList: """定义一个容器类,用于存储任意类型的数据,其内部的存储方式用list实现""" def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return "MyList(%s)" % self.data def __iter__(self): """此方法把MyList类型的对象做为可迭代对象使用,此方法需要返回迭代器""" # return 迭代器 return MyListIterator(self.data) class MyListIterator: """此类定义一个迭代器,用于生成能够访问MyList对象的迭代器""" def __init__(self, lst_data): print("迭代器已经创建") self.data = lst_data # 绑定要访问的数据列表 self.cur_pos = 0 # 设置迭代器的起始位置为0 def __next__(self): """此方法访问可迭代对象的数据,如果没有数据则触发StopIteration异常来通知调用者停止迭代即迭代器协议""" # 判定索引是否越界,如果以越界则触发异常停止迭代 if self.cur_pos >= len(self.data): raise StopIteration index = self.cur_pos self.cur_pos += 1 # 将当前位置向后移动准备下次获取 return self.data[index] # 返回当前位置的数据 myl = MyList([1, 2, 3, 4, 5]) for i in myl: print(i)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 改写示例3完善迭代器 class MyList: """定义一个容器类,用于存储任意类型的数据,其内部的存储方式用list实现""" def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return "MyList(%s)" % self.data def __iter__(self): """此方法把MyList类型的对象做为可迭代对象使用,此方法需要返回迭代器""" self.cur_pos = 0 # 设置迭代器的起始位置为0 # return 迭代器 return self def __next__(self): """此方法访问可迭代对象的数据,如果没有数据则触发StopIteration异常来通知调用者停止迭代即迭代器协议""" # 判定索引是否越界,如果以越界则触发异常停止迭代 if self.cur_pos >= len(self.data): raise StopIteration index = self.cur_pos self.cur_pos += 1 # 将当前位置向后移动准备下次获取 return self.data[index] # 返回当前位置的数据 myl = MyList([1, 2, 3, 4, 5]) it = iter(myl) while True: try: x = next(it) print(x) except StopIteration: break
20.生成器Generator
1.生成器概述:
1.生成器是能够动态提供数据的对象,生成器对象也是可迭代对象
2.生成器有两种: 生成器函数,生成器表达式
3.生成器函数定义: 含有yield语句的函数是生成器函数,此函数被调用将返回一个生成器对象
4.生成器特点: 节省内存, 惰性机制(不到最后不拿值), 只前进不后退
2.生成器函数语法:
yield 表达式 # yield 翻译为(产生或生成)
yield 用于def函数中,目的是将此函数作为生成器函数使用
yield 用来生成数据,供next(it)函数使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 写一个生成器函数my_integer(n)生成 1 到 n的整数 def my_integer(n): i = 1 # 先初始化变量i将其设置为起始数 while i < n: # 循环判断是否已到终止点,如果未到则生成 yield i # 生成整数 i += 1 # 控制循环条件 for x in my_integer(5): print(x) # 1 2 3 4
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成器自动实现了迭代器协议,就是可迭代对象,本身就有内置的__next__()方法 def test(): # 生成器的第一种表现方式,生成器函数的yield返回值 print("第一次") yield 1 print("第二次") yield 2 # yield可以多次返回 g = test() print(g) print(g.__next__()) print(g.__next__()) # 第二次执行从第一次的返回结果后开始执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成器实现fibonacci数列 def create_num(all_num): a, b = 0, 1 current_num = 0 while current_num < all_num: yield a # 如果一个函数中有yield语句,那么这个就不在是函数而是一个生成器模板 a, b = b, a+b current_num += 1 return "最终返回结果" # 如果再调用函数的时候,发现这个函数中有yield,那么此时不是在调用函数,而是创建一个生成器对象 obj1 = create_num(10) obj2 = create_num(5) for num in obj1: print("obj1", num) while True: try: ret = next(obj2) print("obj2:", ret) except Exception as ret: ret = ret.value # 最后return的结果只能在捕获异常里用value拿 print(ret) break
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成器的send传参 def create_num(all_num): a, b = 0, 1 current_num = 0 while current_num < all_num: ret = yield a print(">>>ret:", ret) # >>>ret: coco a, b = b, a+b current_num += 1 obj1 = create_num(10) # obj.send(None) # send一般不会放到第一次启动生成器,如果非要使用,参数传递None ret = next(obj1) print(ret) # 0 # send(x)作用同next(obj)一样让生成器向下执行一次 # send(x)的参数会传递给上次返回位置,当做yield a的结果,然后ret保存这个结果 # send(x)的结果同next(obj)一样是下一次调用yield时yield后面的值 ret = obj1.send("coco") # 第一次调用send带参数会出错,因为没有接收参数的对象 print(ret) # 1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成器实现zip迭代工具函数内部原理 def myzip(iter1, iter2): it1 = iter(iter1) it2 = iter(iter2) try: while True: a = next(it1) b = next(it2) yield (a, b) except: pass numbers = [10086, 10000, 10010, 95588] name = ["中国移动", "中国电信", "中国联通"] for i in myzip(numbers, name): print(i)
3.生成器表达式语法:
gen = (表达式 for 变量 in 可迭代对象 [if 真值表达式]) # 用推导式形式生成一个新的生成器,[]内部的if部分可以省略
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
gen = (x**2 for x in range(1, 5)) # gen绑定生成器对象,此对象为可迭代对象 it = iter(gen) # 返回一个迭代器给it绑定 next(it) # 1 next(it) # 4 next(it) # 9 next(it) # 16 next(it) # StopIteration
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
egg_list3 = ("数字%s" % i for i in range(10)) # 生成器的第二种表现方式: 生成器表达式 print(egg_list3) print(egg_list3.__next__()) print(egg_list3.__next__()) print(next(egg_list3)) print(sum(i for i in range(10000)))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 寻找名字中带有两个e的⼈人的名字 names = ([['Tom', 'Billy', 'Jefferson', 'Andrew', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Sherry', 'Eva']]) # 不用推导式和表达式 result = [] for first in names: for name in first: if name.count("e") >= 2: result.append(name) print(result) # 推导式 gen = (name for first in names for name in first if name.count("e") >= 2) for name in gen: print(name)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 生成器惰性机制,不到最后不拿值 def add(a, b): return a + b def test(): for r_i in range(4): yield r_i g = test() # g是生成器 for n in [2, 10]: g = (add(n, i) for i in g) # 过程分析 # n=2-->g = (add(n, i) for i in g) # n=10-->g = (add(n, i) for i in (add(n, i) for i in g)) # list(g)-->(add(10, i) for i in (add(10, i) for i in test()) # list(g)-->(add(10, i) for i in (add(10, i) for i in [0, 1, 2, 3]) # list(g)-->((add(10, i) for i in [10, 11, 12, 13]) # 到达此步骤才开始取值 print(list(g)) # [20, 21, 22, 23]
21.生产者消费者模型
概述:
1.生产者是一个函数,消费者是另一个函数,内存缓冲区是一个有限的容器,处理函数间的的协作调度便是生产者消费者模型
2.内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者必须等待,其他时候可以是动态平衡
3.可以运用生成器的特性实现协程调度
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 常规的执行流程,先全部生产再全部消耗 import time def producer(): ret = [] for i in range(100): time.sleep(0.03) ret.append("包子%s" % i) return ret def consumer(res): for index, bun in enumerate(res): time.sleep(0.1) print("第%s个人吃了%s" %(index, bun)) res = producer() consumer(res) # 用生成器实现并发,一边生产一边消耗缓解内存压力 import time def consumer(name): print("%s开始准备吃包子" % name) while True: bun = yield # send方法的参数赋值给yield,yield赋值给bun time.sleep(1) print("%s开始吃%s" % (name, bun)) def producer(): c1 = consumer("coco") c2 = consumer("dodo") print("1") c1.__next__() c2.__next__() print("2") for i in range(100): time.sleep(0.1) c1.send("包子%s" % i) # send方法不传参就等于__next__方法,传递参数让yield接收 c2.send("包子%s" % i) producer()
版权:本文版权归作者
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任