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

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

迭代器(iterator)

def add(s, x): return s + xdef gen(): for i in range(4):  yield ibase = gen()for n in [1, 10]: base = (add(i, n) for i in base)print list(base)

这个东西输出可以脑补一下, 结果是[20,21,22,23], 而不是[10, 11, 12, 13]。

要说生成器,必须首先说迭代器

区分iterable,iterator与itertion
讲到迭代器,就需要区别几个概念:iterable,iterator,itertion, 看着都差不多,其实不然。下面区分一下。

itertion: 就是迭代,一个接一个(one after another),是一个通用的概念,比如一个循环遍历某个数组。

iterable: 这个是可迭代对象,属于python的名词,范围也很广,可重复迭代,满足如下其中之一的都是iterable:

  • 可以for循环: for i in iterable
  • 可以按index索引的对象,也就是定义了getitem方法,比如list,str;
  • 定义了iter方法。可以随意返回。
  • 可以调用iter(obj)的对象,并且返回一个iterator

iterator: 迭代器对象,也属于python的名词,只能迭代一次。需要满足如下的迭代器协议

  • 定义了iter方法,但是必须返回自身
  • 定义了next方法,在python3.x是next。用来返回下一个值,并且当没有数据了,抛出StopIteration可以保持当前的状态

首先str和list是iterable 但不是iterator:

In [13]: si = iter(s)In [14]: siOut[14]: 
In [15]: si.__iter__ # 有__iter__Out[15]:
In [16]: si.next #拥有nextOut[16]:
In [20]: si.__iter__() is si #__iter__返回自己Out[20]: TrueIn [1]: %pasteclass DataIter(object): def __init__(self, *args): self.data = list(args) self.ind = 0 def __iter__(self): #返回自身 return self def next(self): # 返回数据 if self.ind == len(self.data): raise StopIteration else: data = self.data[self.ind] self.ind += 1 return data## -- End pasted text --In [9]: d = DataIter(1,2)In [10]: for x in d: # 开始迭代 ....: print x ....:12In [13]: d.next() # 只能迭代一次,再次使用则会抛异常---------------------------------------------------------------------------StopIteration Traceback (most recent call last)----> 1 d.next()
in next(self) 10 def next(self): 11 if self.ind == len(self.data):---> 12 raise StopIteration 13 else: 14 data = self.data[self.ind]

从next函数中只能向前取数据,一次取一个可以看出来,不过不能重复取数据,那这个可不可以解决呢?

我们知道iterator只能迭代一次,但是iterable对象则没有这个限制,因此我们可以把iterator从数据中分离出来,分别定义一个iterable与iterator如下:

class Data(object): # 只是iterable:可迭代对象而不iterator:迭代器 def __init__(self, *args):  self.data = list(args) def __iter__(self): # 并没有返回自身  return DataIterator(self)class DataIterator(object): # iterator: 迭代器 def __init__(self, data):  self.data = data.data  self.ind = 0 def __iter__(self):  return self def next(self):  if self.ind == len(self.data):   raise StopIteration  else:   data = self.data[self.ind]   self.ind += 1   return dataif __name__ == '__main__': d = Data(1, 2, 3) for x in d:  print x, for x in d:  print x,1,2,31,2,3

Python支持在容器上迭代,通过两个方法实现,允许用户自定义,序列总是支持迭代方法.

迭代器的功能可以使用列表代替,但如果有很多值,列表就会占用太多的内存,而如果有可以一个接一个地计算值的函数,那么就可以在使用时采用计算一个值时获取一个值,占用更少内存。

迭代器对于变量,有两个方法iter(),next()

当遍历一个迭代器的时候,它会修改内部状态,导致你只能向前获取下一个元素,不能通过迭代器访问后面一个元素;也就是说当你通过迭代器访问了一个元素以后,在当前循环中不能后退继续访问该元素了,除非你重新生产迭代器对象进行遍历。

#迭代器对于变量list1=[1,2,3,4,5]it1=iter(list1)print(next(it1))print(next(it1))print('=============')for x in it1:    print(x, end='\n')print('=============')#可以使用next()函数list2=list('abcdefg')it2=iter(list2)while True:    try:        print(next(it2))    except StopIteration:        sys.exit()

输出结果如下:

12=============345=============abcdefg

对于要返回迭代器的类,要实现iterator.iter(),iterator.next()

迭代器对象被要求支持下面的两个方法,合起来形成迭代协议。

iterator._iter_()

返回迭代器对象自身。为了允许容器和迭代器被用于for和in语句中,必须实现该方法。

iterator._next_()

返回容器的下一个条目。如果没有更多的条目,抛出StopIteration异常

另外需要注意的是在迭代器中next方法是return下一个元素的值,不像下面介绍的生成器yield一个元素

class Fibs:    def __init__(self):  # 初始化        self.a = 0        self.b = 1    def __next__(self):  # 获取下一个条目        self.a, self.b = self.b, self.a + self.b        return self.a    def __iter__(self):  # 返回迭代器        return selffibs=Fibs()for f in fibs:    if f > 20:        break

生成器(generator)

任何使用yield的函数都称之为生成器.

首先需要明确的就是生成器也是iterator迭代器,因为它遵循了迭代器协议.生成器函数跟普通函数只有一点不一样,就是把 return 换成yield,其中yield是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起。

(如果换成return 函数就返回了)

另外一种说法:生成器就是一个返回迭代器的函数,与普通函数的区别是生成器包含yield语句,更简单点理解生成器就是一个迭代器。

使用yield,可以让函数生成一个序列,该函数返回的对象类型是”generator”,通过该对象连续调用next()方法返回序列值。

生成器函数只有在调用_next()_方法的时候才开始执行函数里面的语句.

在调用count函数时:c=count(7),并不会打印”counting”只有等到调用c.next()时才真正执行里面的语句。每次调用next()方法时,count函数会运行到语句yield n处为止,next()的返回值就是生成值n,此处为了查看返回值打印了出来,可以去掉,再次调用next()方法时,函数继续执行yield之后的语句(熟悉Java的朋友肯定知道Thread.yield()方法,作用是暂停当前线程的运行,让其他线程执行),如:

yield作用就是返回一个生成器,它会保存当前函数状态,记录下一次函数被调用next的时候运行状态

a = (i for i in range(4))print(a)for i in a:    print(i)for i in a:    print('==')    print(i)只输出0,1,2,3
def count(n):    print("cunting")    while n > 0:        print('before yield')        yield n  # 生成值:n        print(n)        n -= 1        print('after yield')c=count(7)c.__next__()#before yieldc.__next__()# 7# after yield# before yieldc.__next__()# 6# after yield# before yield

如果一直调用next方法,当执行到没有可迭代的值后,程序就会报错:

Traceback (most recent call last): File "", line 1, in StopIteration

所以一般不会手动的调用next方法,而使用for循环

for i in count(5):      print (i)
def fibonacci(n): # 生成器函数 - 斐波那契    a, b, counter = 0, 1, 0    while True:        if counter > n:            return        yield a        a, b = b, a + b        counter += 1# f 是一个迭代器,由生成器返回生成f = fibonacci(10)while True:    try:        print(next(f), end=" ")    except StopIteration:        sys.exit()
def fibonacci():    a=b=1    yield a    while True:        a,b = b,a+b        yield b# f=fibonacci()# print(type(fibonacci()))# f.__next__()# f.__next__()for num in fibonacci():    if num > 100:        break    print (num)    time.sleep(1)

可以调试看输出,就明白了。

另外还有一种定义生成器的方法:生成器表达式”()”

my_generator = (x for x in range(3))print(type(my_generator))for i in my_generator:    print(i)

转载地址:http://zndeo.baihongyu.com/

你可能感兴趣的文章
CCBValue
查看>>
C#一些知识点:委托和事件的区别
查看>>
linux修改挂载目录
查看>>
Cocos2d-js-v3.2 在 mac 上配置环境以及编译到 Andorid 的注意事项(转)
查看>>
android开源项目学习
查看>>
提升Mac os x 10.10+xcode6.1之后,Cocoapods发生故障的解决方案
查看>>
Developer Tool - 1. Text Tool and GNU/Linux Tool
查看>>
OAuth 2.0 安全案例回顾
查看>>
标准API使用小技巧
查看>>
jQuery Validate插入 reomte使用详细的说明
查看>>
科普:揭秘手机软件自启原理
查看>>
lintcode :搜索二维矩阵
查看>>
前端设计js+Tab切换可关闭+添加并自动判断是否已打开自动切换当前状态(转载)...
查看>>
for循环,如何结束多层for循环
查看>>
段树 基于单点更新 敌人阵容
查看>>
java中取得上下文路径的方法
查看>>
Tomcat通过配置一个虚拟路径管理web工程
查看>>
如何自定义FusionCharts图表上的工具提示?
查看>>
Spring、Hello Spring
查看>>
JSP的九个隐式对象
查看>>