Python开发【第五篇】迭代器、生成器、递归函数、二分法
阅读目录
一.迭代器
1. 迭代的概念
#迭代器即迭代的工具(自定义的函数),那什么是迭代呢? #迭代:指一个重复的过程,每次重复都可以称之为一次迭代,并且每一次重复的结果是下一个迭代的初始值(例如:罚写作业100遍) while True: #只是单纯地重复,因而不是迭代print('===>') l=[1,2,3] count=0 while count < len(l): #迭代print(l[count])count+=1
2.为何要有迭代器? 什么是可迭代对象? 什么是迭代器对象?
#1、为何要有迭代器? 对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器#2、什么是可迭代对象? 可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下 'hello'.__iter__ (1,2,3).__iter__ [1,2,3].__iter__ {'a':1}.__iter__ {'a','b'}.__iter__ open('a.txt').__iter__#3、什么是迭代器对象? 可迭代对象执行obj.__iter__()得到的结果就是迭代器对象 而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象文件类型是迭代器对象 open('a.txt').__iter__() open('a.txt').__next__()#4、注意: 迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
为何要有迭代器?什么是可迭代对象?什么是迭代器对象?
3.迭代器对象的使用
dic={'a':1,'b':2,'c':3} iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身 iter_dic.__iter__() is iter_dic #Trueprint(iter_dic.__next__()) #等同于next(iter_dic) print(iter_dic.__next__()) #等同于next(iter_dic) print(iter_dic.__next__()) #等同于next(iter_dic) # print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志#有了迭代器,我们就可以不依赖索引迭代取值了 iter_dic=dic.__iter__() while 1:try:k=next(iter_dic)print(dic[k])except StopIteration:break#这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环
迭代器对象的使用
4. for循环原理
#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:print(dic[k])#for循环的工作原理
#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环
5. 迭代器的优缺点
#优点:- 提供一种统一的、不依赖于索引的迭代方式- 惰性计算,节省内存 #缺点:- 无法获取长度(只有在next完毕才知道到底有几个值)- 一次性的,只能往后走,不能往前退
二. 生成器
1. 什么是生成器
生成器:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码 def func():print('====>first')yield 1print('====>second')yield 2print('====>third')yield 3print('====>end') g=func() print(g) #<generator object func at 0x0000000002184360>
2.生成器就是迭代器
g.__iter__ g.__next__ #所以生成器就是迭代器,因此可以这么取值 res=next(g) print(res)
3.生成器Generator总结:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
4.生成器函数
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
import time def generator_fun1():a = 1print('现在定义了a变量')yield ab = 2print('现在又定义了b变量')yield bg1 = generator_fun1() print('g1 : ',g1) #打印g1可以发现g1就是一个生成器 print('-'*20) #我是华丽的分割线 print(next(g1)) time.sleep(1) #sleep一秒看清执行过程 print(next(g1))
5.生成器有什么好处呢?
1.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。
# 假如我想让工厂给学生做校服,生产2000000件衣服,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一件一件的要,也可以根据学生一批一批的找工厂拿。 # 而不能是一说要生产2000000件衣服,工厂就先去做生产2000000件衣服,等回来做好了,学生都毕业了。。。def produce():"""生产衣服"""for i in range(2000000):yield "生产了第%s件衣服"%iproduct_g = produce() print(product_g.__next__()) #要一件衣服 print(product_g.__next__()) #再要一件衣服 print(product_g.__next__()) #再要一件衣服 num = 0 for i in product_g: #要一批衣服,比如5件print(i)num +=1if num == 5:break#到这里我们找工厂拿了8件衣服,我一共让生产函数(也就是produce生成器函数)生产2000000件衣服。 #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
示例
6.练习
#1、自定义函数模拟range(1,7,2)#2、模拟管道,实现时时获取文件中最新内容
# 1、自定义函数模拟range(1,7,2) def myRange(start,stop,step=1):while start < stop:yield startstart += stepobj = myRange(1,7,2) print(next(obj)) print(next(obj)) print(next(obj)) print(next(obj)) #StopIteration#2、模拟管道,实现时时获取文件中最新内容 import time def tail(filename):with open(filename,'r',encoding='utf-8')as f:f.seek(0,2) #从文件末尾开始读取while True:line = f.readline()if not line:time.sleep(0.5)continueyield lineobj = tail('a.txt') for i in obj:print(i)
代码示例
7.协程函数
什么是协程:
协程是一个无优先级的子程序调度组件,允许子程序在特点的地方挂起恢复。(类似于看电影时的暂停播放)
线程包含于进程,协程包含于线程。只要内存足够,一个线程中可以有任意多个协程,但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。
#yield关键字的另外一种使用形式:表达式形式的yield def eater(name):print('%s 准备开始吃饭啦' % name)food_list=[]while True:food = yield food_listprint('%s 吃了 %s' % (name, food))food_list.append(food)e=eater('钢蛋') print(e.send(None)) #初始化,对于表达式形式的yield,在使用时,第一次必须传None,e.send(None)等同于next(e) print(e.send('包子')) print(e.send('韭菜馅包子')) print(e.send('大蒜包子')) e.close() #关闭 print(e.send('大麻花'))#send 获取下一个值的效果和next基本一致 #只是在获取下一个值的时候,给上一yield的位置传递一个数据 #使用send的注意事项# 第一次使用生成器的时候 是用send(None)或者 next进行初始化
8.练习:
1、编写装饰器,实现初始化协程函数的功能
def init(func):def inner(*args,**kwargs):res = func(*args,**kwargs)next(res) #在装饰器中执行初始化方法return resreturn inner@init def eater(name):print('%s 准备开始吃饭啦' % name)food_list=[]while True:food = yield food_listprint('%s 吃了 %s' % (name, food))food_list.append(food)e=eater('钢蛋') # e.send(None) print(e.send('包子')) print(e.send('韭菜馅包子')) print(e.send('大蒜包子'))
装饰器实现初始化协程方法
9. yield 关键字 总结
1、把函数做成迭代器 2、对比return,可以返回多次值,可以挂起/保存函数的运行状态
三. 列表推导式、生成器表达式
1.列表推导式
#1、示例 egg_list=[] for i in range(10):egg_list.append('鸡蛋%s' %i)print(egg_list)#列表推导式1 egg_list = ['臭鸡蛋%s' %i for i in range(0,10) ] print(egg_list)#列表推导式2 egg_list = ['臭鸡蛋%s' %i for i in range(0,10) if i>6] print(egg_list)#2、语法 [expression for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 ... for itemN in iterableN if conditionN ] 类似于 res=[] for item1 in iterable1:if condition1:for item2 in iterable2:if condition2...for itemN in iterableN:if conditionN:res.append(expression)#3、优点:方便,改变了编程习惯,可称之为声明式编程
2.生成器表达式
#生成器表达式 #1、把列表推导式的[]换成()就是生成器表达式#2、示例:生一筐鸡蛋变成给你一只老母鸡,用的时候就下蛋,这也是生成器的特性 chicken=('鸡蛋%s' %i for i in range(5))print(chicken) # generator object <genexpr> at 0x10143f200>print(next(chicken)) #'鸡蛋0'print(list(chicken)) #因chicken可迭代,因而可以转成列表 ['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4',]#3、优点:省内存,一次只产生一个值在内存中
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
4.声明式编程练习题
1、将names=['egon','alex_sb','wupeiqi','yuanhao']中的名字全部变大写2、将names=['egon','alex_sb','wupeiqi','yuanhao']中以sb结尾的名字过滤掉,然后保存剩下的名字长度3、求文件a.txt中最长的行的长度(长度按字符个数算,需要使用max函数)4、求文件a.txt中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数)5、思考题 with open('a.txt') as f:g=(len(line) for line in f) print(sum(g)) #为何报错?
# 1、将names=['egon','alex_sb','wupeiqi','yuanhao']中的名字全部变大写 names=['egon','alex_sb','wupeiqi','yuanhao'] names = [name.upper() for name in names] print([name.upper() for name in names])# 2、将names=['egon','alex_sb','wupeiqi','yuanhao']中以sb结尾的名字过滤掉,然后保存剩下的名字长度 names=['egon','alex_sb','wupeiqi','yuanhao'] names = [len(name) for name in names if not name.endswith("sb") ] print(names) # 3、求文件a.txt中最长的行的长度(长度按字符个数算,需要使用max函数) with open('a.txt','r',encoding='utf-8')as f:print(max(len(i) for i in f)) #最后有一个换行符# 4、求文件a.txt中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数) with open('a.txt','r',encoding='utf-8') as f:print(sum(len(i) for i in f))print(sum(len(i) for i in f)) #原因读取文件的指针已经到文件的末尾了 # 5、思考题with open('a.txt') as f:g=(len(line) for line in f)print(sum(g)) #为何报错? #答: with open 在执行完文件操作后,会自动关闭掉此文件,所以再使用文件内容时会报错
代码示例
四.递归函数
1. 递归调用的定义
#递归调用是函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身,就是递归调用
2.递归分为两个阶段:递推,回溯
#问年龄游戏 #图解。。。 # age(4) = age(3) + 2 # age(3) = age(2) + 2 # age(2) = age(1) + 2 # age(1) = 40def age(n):if n == 1:return 40else:return age(n-1)+2print(age(4))
3.递归的使用
#总结递归的使用: 1. 必须有一个明确的结束条件2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)4.递归默认调用的最大深度为 ---997
4.设置递归最大深度
#设置递归最大深度
#注意:实际上可以达到的深度 取决于计算机的性能了
import sys
sys.setrecursionlimit(10000) #递归最大深度def func1(n):print(">>>>>>",n)n+=1func1(n)func1(0)
5.练习题
1.使用递归函数完成三级菜单
menu = {'北京': {'海淀': {'五道口': {'soho': {},'网易': {},'google': {}},'中关村': {'爱奇艺': {},'汽车之家': {},'youku': {},},'上地': {'百度': {},},},'昌平': {'沙河': {'老男孩': {},'北航': {},},'天通苑': {},'回龙观': {},},'朝阳': {},'东城': {},},'上海': {'闵行': {"人民广场": {'炸鸡店': {}}},'闸北': {'火车战': {'携程': {}}},'浦东': {},},'山东': {}, }
menu
def threeLM(dic):while True:for k in dic:print(k)key = input('input>>').strip()if key == 'b' or key == 'q':return keyelif key in dic.keys() and dic[key]:ret = threeLM(dic[key])if ret == 'q': return 'q'threeLM(menu) #递归函数实现三级菜单
递归函数实现三级菜单
五. 二分查找法
想从一个按照从小到大排列的数字列表中找到指定的数字,遍历的效率太低,用二分法(算法的一种,算法是解决问题的方法)可以极大低缩小问题规模
二分查找法 简单版
#二分查找法
l=[1,2,10,30,33,99,101,200,301,402] #从小到大排列的数字列表
count = 0 #
def search(num,l):global countcount+=1if l:print(l) #查看列表的变化mid = (len(l)-1)//2if num > l[mid]:l=l[mid+1:] # 取列表右边数据elif num < l[mid]:l =l[:mid] # 取列表左边数据else:print(l[mid])return #通过return 终止递归search(num,l) #递归循环调用else:print("没有找到指定数字")search(10,l)
print(count)
二分查找法升级版
#二分查找法 升级版
l=[1,2,10,30,33,99,101,200,301,402]def search(num,l,start=0,stop=len(l)-1):if start <= stop:mid=start+(stop-start)//2print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' %(start,stop,mid,l[mid]))if num > l[mid]:start=mid+1elif num < l[mid]:stop=mid-1else:print('find it',mid)returnsearch(num,l,start,stop)else: #如果stop > start则意味着列表实际上已经全部切完,即切为空print('not exists')returnsearch(101,l)
六 练习
#1.使用递归打印斐波那契数列(前两个数的和得到第三个数,如:0 1 1 2 3 4 7...)#2.一个嵌套很多层的列表,如l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]],用递归取出所有的值
#1.使用递归打印斐波那契数列(前两个数的和得到第三个数,如:0 1 1 2 3 4 7...)#非递归 def fib(n):a,b=0,1while a < n:print(a,end=' ')a,b=b,a+bprint()fib(10) #递归 def fib(a,b,stop):if a > stop:returnprint(a,end=' ')fib(b,a+b,stop)fib(0,1,10)#2. 一个嵌套很多层的列表,如l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]],用递归取出所有的值 l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]]def get(seq):for item in seq:if type(item) is list:get(item)else:print(item) get(l)
代码示例
转载于:https://www.cnblogs.com/wangfengming/p/8352835.html
Python开发【第五篇】迭代器、生成器、递归函数、二分法相关推荐
- Python开发【第一篇】:目录
本系列博文改编自武沛齐老师的原创博文,主要包含 Python基础.前端开发.Web框架.缓存以及队列等内容 ,用于学习记录成长!!! Python开发[第一篇]:目录 Python开发[第二篇]:初 ...
- Python开发【第二篇】:初识Python
Python开发[第二篇]:初识Python Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为 ...
- python django开发工具_利用pyCharm编辑器创建Django项目开发环境-python开发工具第一篇...
[前置说明] 1.django环境与python对应关系: Django version Python versions 1.11 2.7, 3.4, 3.5, 3.6, 3.7 (added in ...
- Agv、Rgv 车辆控制调度系统开发第五篇-避碰
Agv.Rgv 车辆控制调度系统开发第五篇-避碰 前言 上期结束的时候说讲避碰,这期就主要谈一下避碰的原理,避碰是之前给其他人讲调度时,别人提了一个场景里面有三种车,10种货架问我怎么调度,当时确实被 ...
- Python中的装饰器,迭代器,生成器
1. 装饰器 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象. 强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式 装饰器的目标:在遵循1和2的 ...
- 如何确定python开发环境已经配置好_python学习第一天:window安装python开发环境完整篇...
Python是跨平台的,它可以运行在Windows.Mac和各种Linux/Unix系统上: 要开始学习Python编程,首先就得把Python安装到电脑里.安装后,你会得到Python解释器(就是负 ...
- Python开发【第一篇】:初识Python
内容概要 如何编写变量.变量的命名规范 常量(写法) 变量的数据类型(int, str, bool) 用户交互(input) 流程控制(让程序有不同的方向可以选择) 循环(while) 1.变量 变量 ...
- Python开发【第二篇】:Python基本数据类型
运算符 设定:a=10,b=20 . 算数运算 2.比较运算 3.赋值运算 4.逻辑运算 5.成员运算 基本数据类型 1.数字 int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**3 ...
- 夜光带你走进python开发 (五十五)传奇语言
夜光序言: Do not worry about smiling, my mouth hardly ever smiles, but it doesn't mean I'm not smiling i ...
- 电子钱包 java_电子钱包的消费——java card开发第五篇
先来看看流程图: 会发现与圈存的流程大致差不多,只不过有一个很大的差别就是mac值的生成,与圈存不同的是,消费流程中终端首先给卡片发送消费初始化命令,卡片收到命令之后并不会产生mac1的值,而只是产生 ...
最新文章
- 扫地机器人能有多硬核?好家伙自动驾驶、激光扫描、NLP这些硬科技全上了,科沃斯:技术创新才能打破行业内卷...
- 改变Eclipse主题颜色
- 芒果TV会员,月卡最低9.9元,年卡最低128元!
- mysql_num_rows+报错_错误:警告:mysql_num_rows()期望参数1为资源,在第19行的C:\ xampp...
- JavaScript学习(七十七)—统计字符串中出现次数最多的字符和每个字符出现的次数
- 控制台报错:java.lang.ClassNotFoundException: javax.xml.bind.JAXBException之解决方法
- java jdom追加节点_java-使用xpath和jdom选择一个节点
- python字符串格式化 说明符顺序_python实践分享:格式化字符串时使用.format方式还是“%”...
- 英特尔发布全新英特尔® INDE 2015工具套件
- 查询任意汉字的Unicode编码,UTF8编码,GB2312编码,GBK编码
- 广州地铁的速度与激情
- ct扫描方式有哪些_日联科技x-ray:工业CT是怎么进行X射线的断层扫描的
- VB显示透明FLASH效果
- MYSQL-Front新手连接数据库总结
- 使用nginx配置二级域名
- emacs下使用google-cpplint
- 原创超简单代码(1.18.50)
- 桌面便利贴软件下载 电脑桌面便签小工具软件下载
- against fate
- 使用ESP8266与小爱同学通过Arduino控制舵机
热门文章
- easyui的datagrid和panel如何让标题动态改变?
- Android 自定义ViewPager设置屏蔽左右滑动事件
- .NET中如何深度判断2个对象相等
- 2-Eighteenth Scrum Meeting-20151218
- jquery animate自定义动画
- [jQuery基础] jQuery案例 -- qq音乐以及初步解决Ajax 跨域问题
- 【ES6】Set Map数据结构、Iterator遍历器
- PyTorch入门(二)--实现简单神经网络
- php iso 8859 1 解码,关于php:Apache的默认编码是ISO-8859-1,但网站是UTF-8?
- Modularity(模块化-ES6)