博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
迭代器和生成器
阅读量:5876 次
发布时间:2019-06-19

本文共 9633 字,大约阅读时间需要 32 分钟。

迭代器(iterator) 

迭代器协议:必须拥有__iter__方法和__next__方法

迭代的概念

#迭代器即迭代的工具,那什么是迭代呢?#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值while True: #只是单纯地重复,因而不是迭代    print('===>')     L=[1,2,3]count=0while count < len(L): #迭代    print(l[count])    count+=1
#dir() ----(展示所拥有的所有方法) print(dir([1,2])) print(dir((2,3)))print(dir({
'a':1,'b':2}))print(dir({1,2,3,4}))##以下所展示的有双下方法:如'__add__', '__class__'它们是Python解释器的内部方法,也称为双下方法。是由C语言写的,可能不止一种方式调用它。
# ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] # ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] # ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

##双下方法:

print([1].__add__([2]))print([1]+[2])###[1, 2][1, 2]

 由上可以看出,可以被for循环的都是可迭代的,要想可迭代,内部必须要有一个__iter__方法。

首先,判断 int 类型有没有__iter__方法:

print("__iter__" in dir(int))# False

##执行以下__iter__方法:

print([1,2].__iter__())# 
#生产一个迭代器

得到了一个list_iterator,此时我们又得到了一个新的名词——iterator。(迭代器)

同样的,可以对字典、集合、元祖进行__iter__操作

print((1,2,3).__iter__())print({
'a':1,'b':2}.__iter__())print({1,2,3}.__iter__())#
#
#

同样的得到了tuple_iterator、dict_keyiterator、set_iterator,这就说明了它们都是可迭代的。

可以通过集合求差集来看迭代器与可迭代对象质检的差别。

print(set(dir([1,2].__iter__()))-set(dir([1,2])))# {'__next__', '__length_hint__', '__setstate__'}
 

迭代器:必须拥有__iter__方法和__next__方法

print("__iter__" in dir([].__iter__()))print("__next__" in dir([].__iter__()))###TrueTrue
############ from collections import Iterablefrom collections import Iteratorprint(isinstance([],Iterator))print(isinstance([],Iterable ))############FalseTrue列表是可迭代的,但不是一个迭代器

#####或者创造一个数据类型:

from collections import Iterablefrom collections import Iteratorclass A:    def __iter__(self):pass    def __next__(self):passa = A()print(isinstance(a,Iterator))print(isinstance(a,Iterable ))###TrueTrue 再次说明,迭代器:必须拥有__iter__方法和__next__方法 PS:可迭代的.__iter__()方法就可以得到一个迭代器   迭代器中的__next__()方法可以一个一个获取值

######################################补充说明###############################################

#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、注意:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

迭代器的作用:

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循环

for循环:(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,结束循环

 迭代器的优缺点:

#优点:  - 提供一种统一的、不依赖于索引的迭代方式 (方便使用,从容器数据类型中一个一个取值,并把所有的值都取到)  - 惰性计算,节省内存(迭代器并不会在内存中占用一大块内存来保存数据,而是随着循环,每次只生成一个数据)#缺点:  - 无法获取长度(只有在next完毕才知道到底有几个值)  - 一次性的,只能往后走,不能往前退

 #只有可迭代对象的时候才能用for循环

#当遇到一个变量,不确定是否能用for循环是,先判断它是否可迭代

#############################################################################################

生成器

什么是生成器(生成器的本质是迭代器)

 
#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码 #含有yield关键字的函数就是 生成器函数 #调用生成器函数的时候,函数不执行,而是返回一个生成器 #每次调研阶段next方法,就会取到一个值得 #直达取到最后一个,再采用next方法就会报错 def generator():
print('====>first')    yield 1    print('====>second')    yield 2    print('====>third')    yield 3    print('====>end')g = generator()print(g) #
print(g.__next__())print(g.__next__())print(g.__next__()) ################ 或者: for i in g:   print(i)#####
====>first1====>second2====>third3

 ##wahaha:

# 哇哈哈%idef wahaha():    for i in range(100):        yield "哇哈哈%d"%ig = wahaha()count = 0for i in g:    count += 1    print(i)    if count > 10:        breakprint("****",g.__next__()) #生成器记录当前的位置以及下一步要走的位置###哇哈哈0哇哈哈1哇哈哈2哇哈哈3哇哈哈4哇哈哈5哇哈哈6哇哈哈7哇哈哈8哇哈哈9哇哈哈10**** 哇哈哈11
#监听过滤文件,文件有“Python”就打印def tail(filename):    f = open(filename,encoding="utf-8")    while 1:        line = f.readline()        if line.strip():            yield line.strip()g = tail("file")for i in g:    if "python" in i:        print("$$$$$",i)

 生成器表现形式:

1.生成器函数

#含有yield关键字的函数就是生成器函数

#特点:调用函数之后,函数不执行,而是返回一个生成器;每次调用next方法的时候会取到一个值;直到取到最后一个,再执行next会报错

2.生成器表达式

#从生成器中取值的方法:next;for;数据类型的强制转换,占内存。

生成器进阶:

send获取下一个值的效果和next基本一样只是在获取下一个值的时候,给上一个yield的位置传递一个数据

使用send的注意事项

1)、send作用范围和next一样

2)、第一次使用生成器的时候,是用next方法获取下一个值

3)、最后一个yield不能接受外部的值

def generator():    print(123)    content=yield 1    print("========",content)    arg = yield 2    ''''''    yield g = generator()ret = g.__next__()print("$$$$$$$$",ret)ret = g.send("Hello!") # send 的效果和next一样print("########",ret)###123$$$$$$$$ 1======== Hello!######## 2

##用生成器,获取移动平均值#移动平均值

def average():    sum = 0    count = 0    avg = 0    while 1:        num = yield avg        sum += num        count += 1        avg = sum/countavg_g = average()avg_g.__next__()print(avg_g.send(10))print(avg_g.send(20) print(avg_g.send(30))
### 10.0 15.0 20.0
#带装饰器的移动平均值;#在调用被装饰生成器函数的时候首先用next激活生成器 #预激生成器:def init(func):    def inner(*args,**kwargs):        g = func(*args,**kwargs)        next(g)  #g.__next__()        return g    return inner@initdef average():    sum = 0    count = 0    avg = 0    while 1:        num = yield avg        sum += num        count += 1        avg = sum/countavg_g = average()print(avg_g.send(10))print(avg_g.send(20))print(avg_g.send(30))
### 10.0 15.0 20.0
#在调用被装饰生成器函数的时候首先用next激活生成器

 ###yield from

##
def gen1():     for c in 'AB':         yield c     for i in range(3):         yield i print(list(gen1())) #数据类型的强制转换 #['A', 'B', 0, 1, 2] ## def gen2():     yield from 'AB'     yield from range(3) print(list(gen2())) ##

['A', 'B', 0, 1, 2]

#python3的新功能(yield from)def generator():    a = "AB"    b = "22"    yield from a    yield from bg = generator()for i in g:    print(i)##AB22

列表推导式和生成器表达式

#列表推导式egg_list = ["egg %d" %i for i in range(10)]print(egg_list)#或者用for循环egg_list = []for i in range(10):    egg_list.append("egg %d" %i)print(egg_list)print([i*i for i in range(10)])###['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9']['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9'][0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

##生成器表达式

#生成器表达式g = (i for i in range(10))print(g) #返回一个生成器,几乎不占用内存for i in g:    print(i)##
at 0x00000262E6CE6308>0123456789

 ###各种推导式:

[满足条件的元素  for  元素  in  可迭代的数据类型  if  元素满足的相关条件 ] #筛选功能

例一:30以内所有能被3整除的数

multiples = [i for i in range(30) if i % 3 is 0]print(multiples)# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

例二:30以内所有能被3整除的数的平方

def squared(x):    return x*xmultiples = [squared(i) for i in range(30) if i % 3 is 0]print(multiples) 或者:
lst = [i*i for i in range(30) if i % 3 ==0]print(lst)##[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

例三:找到嵌套列表中名字含有两个‘e’的所有名字

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]print([name for lst in names for name in lst if name.count('e') >= 2])  # 注意遍历顺序,这是实现的关键 #['Jefferson', 'Wesley', 'Steven', 'Jennifer']

 

 

转载于:https://www.cnblogs.com/shaopan/p/10015997.html

你可能感兴趣的文章
js闭包
查看>>
寒假。3.3.G - Common Child (最大公共子序)
查看>>
设计模式学习笔记--原型模式
查看>>
.Net 通过MySQLDriverCS操作MySQL
查看>>
JS Cookie
查看>>
ubuntu Unable to locate package sysv-rc-conf
查看>>
笔记:认识.NET平台
查看>>
cocos2d中CCAnimation的使用(cocos2d 1.0以上版本)
查看>>
【吉光片羽】短信验证
查看>>
MacBook如何用Parallels Desktop安装windows7/8
查看>>
gitlab 完整部署实例
查看>>
GNS关于IPS&ASA&PIX&Junos的配置
查看>>
七天学会ASP.NET MVC (四)——用户授权认证问题
查看>>
upgrade to iOS7,how to remove stroyboard?
查看>>
影响企业信息化成败的几点因素
查看>>
SCCM 2016 配置管理系列(Part8)
查看>>
zabbix监控部署
查看>>
struts中的xwork源码下载地址
查看>>
Android硬件抽象层(HAL)深入剖析(二)
查看>>
CDays–4 习题一至四及相关内容解析。
查看>>