各种推导式详解
2018-06-17 23:27:17来源:未知 阅读 ()
什么是迭代 (iterable)
字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。
可以直接作用于for循环的对象统称为可迭代对象(Iterable)。
可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。
所有的Iterable均可以通过内置函数iter()来转变为Iterator。
对迭代器来讲,有一个__next__()就够了。在你使用for 和 in 语句时,程序就会自动调用即将被处理的对象的迭代器对象,然后使用它的__next__()方法,直到监测到一个StopIteration异常。
可迭代协议
我们现在是从结果分析原因,能被for循环的就是“可迭代的”,但是如果正着想,for怎么知道谁是可迭代的呢?
假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。
1 >>> L = [1,2,3] 2 >>> [x**2 for x in L] 3 [1, 4, 9] 4 >>> next(L) 5 Traceback (most recent call last): 6 File "<stdin>", line 1, in <module> 7 TypeError: 'list' object is not an iterator 8 >>> I=iter(L) 9 >>> next(I) 10 1 11 >>> next(I) 12 2 13 >>> next(I) 14 3 15 >>> next(I) 16 Traceback (most recent call last): 17 File "<stdin>", line 1, in <module> 18 StopIteration
上面例子中,列表L可以被for进行循环但是不能被内置函数next()用来查找下一个值,所以L是Iterable。
L通过iter进行包装后设为I,I可以被next()用来查找下一个值,所以I是Iterator。
- 内置函数iter()仅仅是调用了对象的__iter()方法,所以list对象内部一定存在方法iter__()
- 内置函数next()仅仅是调用了对象的__next()方法,所以list对象内部一定不存在方法next__(),但是Itrator中一定存在这个方法。
- for循环内部事实上就是先调用iter()把Iterable变成Iterator在进行循环迭代的
1 >>> L = [4,5,6] 2 >>> I = L.__iter__() 3 >>> L.__next__() 4 Traceback (most recent call last): 5 File "<stdin>", line 1, in <module> 6 AttributeError: 'list' object has no attribute '__next__' 7 >>> I.__next__() 8 4 9 >>> from collections import Iterator, Iterable 10 >>> isinstance(L, Iterable) 11 True 12 >>> isinstance(L, Iterator) 13 False 14 >>> isinstance(I, Iterable) 15 True 16 >>> isinstance(I, Iterator) 17 True 18 >>> [x**2 for x in I] 19 [25, 36]
迭代器
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
具有访问生成器的能力,可以访问到生成器的值,类似于生成器的__next__
方法,一个一个值一个值得去迭代,只能够按照顺序的去查找。
特点:
- 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
- 不能随机访问集合中的某个值 ,只能从头到尾依次访问
- 访问到一半时不能往回退
- 便于循环比较大的数据集合,节省内存
1 print('__next__' in dir(range(12))) #查看'__next__'是不是在range()方法执行之后内部是否有__next__ 2 print('__iter__' in dir(range(12))) #查看'__next__'是不是在range()方法执行之后内部是否有__next__ 3 4 from collections import Iterator 5 print(isinstance(range(100000000),Iterator)) #验证range执行之后得到的结果不是一个迭代器
yield from
1 def gen1(): 2 for c in 'AB': 3 yield c 4 for i in range(3): 5 yield i 6 7 print(list(gen1())) 8 9 def gen2(): 10 yield from 'AB' 11 yield from range(3) 12 13 print(list(gen2()))
生成器
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
生成器函数
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
仅仅拥有生成某种东西的能力,如果不用__next__
方法是获取不到值得。
创建一个生成器函数
1 >>> def scq(): 2 print("11") 3 # 当函数代码块中遇到yield关键字的时候,这个函数就是一个生成器函数 4 yield 1 5 print("22") 6 yield 2 7 print("33") 8 yield 3 9 10 11 # 把生成器赋值给一个对象 12 13 >>> r = scq() 14 15 # 查看r的苏剧类型并且输出r的值 16 17 >>> print(type(r),r) 18 <class 'generator'> <generator object scq at 0x000001F117D8DF10> 19 20 # 当执行生成器的__next__的时候,代码会按照顺序去执行,当执行到yield时会返回并 21 # 提出,yield后面的值就是返回值,然后记录代码执行的位置,并退出 22 23 >>> ret = r.__next__() 24 11 25 26 # 第二次执行的时候会根据上次代码执行的位置继续往下执行 27 28 >>> ret = r.__next__() 29 22 30 >>> ret = r.__next__() 31 33 32 33 # 如果__next__获取不到值的时候就会报StopIteration错误 34 35 >>> ret = r.__next__() 36 Traceback (most recent call last): 37 File "<stdin>", line 1, in <module> 38 StopIteration
利用生成器创建一个range
1 # 创建一个生成器函数,函数名是range,n是传入的参数,也是输出的数的最大值 2 def range(n): 3 # 默认从0开始 4 start = 0 5 # 进入while循环,如果最小值小于最大值就进入循环 6 while start < n: 7 # 第一次返回start,下面代码不执行 8 yield start 9 # 第二次进来的时候start = start + 1,然后进入下一次循环 10 start += 1 11 12 # 停止的参数为5 13 obj = range(5) 14 # 第一个数赋值给n1 15 n1 = obj.__next__() 16 # 第二个数赋值给n2 17 n2 = obj.__next__() 18 # 第三个数赋值给n3 19 n3 = obj.__next__() 20 # 第四个数赋值给n4 21 n4 = obj.__next__() 22 # 第五个数赋值给n5 23 n5 = obj.__next__() 24 25 # 输出这五个数的值 26 print(n1,n2,n3,n4,n5) 27 28 # 执行结果 29 30 C:\Python35\python.exe F:/Python_code/sublime/Week5/Day03/s1.py 31 0 1 2 3 4 32 33 Process finished with exit code 0
生成器监听文件输入的例题
1 import time 2 3 4 def tail(filename): 5 f = open(filename) 6 f.seek(0, 2) #从文件末尾算起 7 while True: 8 line = f.readline() # 读取文件中新的文本行 9 if not line: 10 time.sleep(0.1) 11 continue 12 yield line 13 14 tail_g = tail('tmp') 15 for line in tail_g: 16 print(line)
计算移动平均值(1)
1 def averager(): 2 total = 0.0 3 count = 0 4 average = None 5 while True: 6 term = yield average 7 total += term 8 count += 1 9 average = total/count 10 11 12 g_avg = averager() 13 next(g_avg) 14 print(g_avg.send(10)) 15 print(g_avg.send(30)) 16 print(g_avg.send(5))
计算移动平均值(2)预激协程的装饰器
1 def init(func): #在调用被装饰生成器函数的时候首先用next激活生成器 2 def inner(*args,**kwargs): 3 g = func(*args,**kwargs) 4 next(g) 5 return g 6 return inner 7 8 @init 9 def averager(): 10 total = 0.0 11 count = 0 12 average = None 13 while True: 14 term = yield average 15 total += term 16 count += 1 17 average = total/count 18 19 20 g_avg = averager() 21 # next(g_avg) 在装饰器中执行了next方法 22 print(g_avg.send(10)) 23 print(g_avg.send(30)) 24 print(g_avg.send(5))
列表推导式和生成器表达式
1 #老男孩由于峰哥的强势加盟很快走上了上市之路,alex思来想去决定下几个鸡蛋来报答峰哥 2 3 egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析 4 5 #峰哥瞅着alex下的一筐鸡蛋,捂住了鼻子,说了句:哥,你还是给我只母鸡吧,我自己回家下 6 7 laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式 8 print(laomuji) 9 print(next(laomuji)) #next本质就是调用__next__ 10 print(laomuji.__next__()) 11 print(next(laomuji))
各种推导式详解
推导式的套路
之前我们已经学习了最简单的列表推导式和生成器表达式。但是除此之外,其实还有字典推导式、集合推导式等等。
下面是一个以列表推导式为例的推导式详细格式,同样适用于其他推导式。
1 variable = [out_exp_res for out_exp in input_list if out_exp == 2] 2 out_exp_res: 列表生成元素表达式,可以是有返回值的函数。 3 for out_exp in input_list: 迭代input_list将out_exp传入out_exp_res表达式中。 4 if out_exp == 2: 根据条件过滤哪些值可以。
列表推导式
例一:30以内所有能被3整除的数
1 multiples = [i for i in range(30) if i % 3 is 0] 2 print(multiples) 3 # Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
例二:30以内所有能被3整除的数的平方
1 def squared(x): 2 return x*x 3 multiples = [squared(i) for i in range(30) if i % 3 is 0] 4 print(multiples)
例三:找到嵌套列表中名字含有两个‘e’的所有名字
1 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], 2 ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] 3 4 print([name for lst in names for name in lst if name.count('e') >= 2]) # 注意遍历顺序,这是实现的关键
字典推导式
例一:将一个字典的key和value对调
1 mcase = {'a': 10, 'b': 34} 2 mcase_frequency = {mcase[k]: k for k in mcase} 3 print(mcase_frequency)
例二:合并大小写对应的value值,将k统一成小写
1 mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} 2 mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()} 3 print(mcase_frequency)
集合推导式
例:计算列表中每个值的平方,自带去重功能
1 squared = {x**2 for x in [1, -1, 2]} 2 print(squared) 3 # Output: set([1, 4])
练习题:
例1: 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
例2: 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
例3: 求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]]
1 1.[name.upper() for name in names if len(name)>3] 2 2.[(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1] 3 3. [row[2] for row in M]
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
1 sum(x ** 2 for x in range(4)) 2 3 # 而不用多此一举的先构造一个列表: 4 5 sum([x ** 2 for x in range(4)])
可迭代对象:
拥有__iter__方法
特点:惰性运算
例如:range(),str,list,tuple,dict,set
迭代器Iterator:
拥有__iter__方法和__next__方法
例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o
生成器Generator:
本质:迭代器,所以拥有__iter__方法和__next__方法
特点:惰性运算,开发者自定义
使用生成器的优点:
1.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。
2.提高代码可读性
1 #列表解析 2 sum([i for i in range(100000000)])#内存占用大,机器容易卡死 3 4 #生成器表达式 5 sum(i for i in range(100000000))#几乎不占内存
生成器相关的面试题
生成器在编程中发生了很多的作用,善用生成器可以帮助我们解决很多复杂的问题
除此之外,生成器也是面试题中的重点,在完成一些功能之外,人们也想出了很多魔性的面试题。
面试题(1)
1 def demo(): 2 for i in range(4): 3 yield i 4 5 g=demo() 6 7 g1=(i for i in g) 8 g2=(i for i in g1) 9 10 print(list(g1)) 11 print(list(g2))
面试题(2)
1 def add(n,i): 2 return n+i 3 4 def test(): 5 for i in range(4): 6 yield i 7 8 g=test() 9 for n in [1,10]: 10 g=(add(n,i) for i in g) 11 12 print(list(g))
tail&grep
1 def demo(): 2 for i in range(4): 3 yield i 4 5 g=demo() 6 7 g1=(i for i in g) 8 g2=(i for i in g1) 9 10 print(list(g1)) 11 print(list(g2)) 12 13 复制代码 14 复制代码 15 16 def add(n,i): 17 return n+i 18 19 def test(): 20 for i in range(4): 21 yield i 22 23 g=test() 24 for n in [1,10]: 25 g=(add(n,i) for i in g) 26 27 print(list(g)) 28 29 复制代码 30 复制代码 31 32 import os 33 34 def init(func): 35 def wrapper(*args,**kwargs): 36 g=func(*args,**kwargs) 37 next(g) 38 return g 39 return wrapper 40 41 @init 42 def list_files(target): 43 while 1: 44 dir_to_search=yield 45 for top_dir,dir,files in os.walk(dir_to_search): 46 for file in files: 47 target.send(os.path.join(top_dir,file)) 48 @init 49 def opener(target): 50 while 1: 51 file=yield 52 fn=open(file) 53 target.send((file,fn)) 54 @init 55 def cat(target): 56 while 1: 57 file,fn=yield 58 for line in fn: 59 target.send((file,line)) 60 61 @init 62 def grep(pattern,target): 63 while 1: 64 file,line=yield 65 if pattern in line: 66 target.send(file) 67 @init 68 def printer(): 69 while 1: 70 file=yield 71 if file: 72 print(file) 73 74 g=list_files(opener(cat(grep('python',printer())))) 75 76 g.send('/test1') 77 78 协程应用:grep -rl /dir
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:Python的re模块
- Django模版中加载静态文件配置详解 2019-07-24
- Python 运算符 各类运算符总结 2019-07-24
- Frangi形态学滤波详解 2019-07-24
- 双线性插值-推导和应用 2019-07-24
- day14-推导式和生成器表达式 2019-06-13
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash