摘要:来说说迭代器和生成器,还有可迭代对象和生成器表达式。有点绕是不是,其实,一般只要知道可迭代对象以及它是如何实现的就行了,中常常用生成器来代替迭代器,可以说,生成器就是迭代器。
来说说迭代器和生成器,还有可迭代对象和生成器表达式。
之前简单的提到过,一个对象是可迭代的可以理解为能够使用for循环。这样说其实不太准确,某个对象可迭代是因为它内部实现了$__iter__$这个特殊方法。比如在python中,序列类型(列表,元组这些)都是可以迭代的,因为内部都有$__iter__$方法的实现。
不过,其实我们不用特别的实现$__iter__$方法,只要实现了$__getitem__$方法就可以。这个方法我之前介绍过,实现它之后就可以进行切片以及迭代操作。为什么?因为在对一个对象迭代时如果找不到$__iter__$方法,python会自动去寻找$__getitem__$方法,然后构造一个迭代器,从0开始获取元素。
说了半天,迭代器又是什么东西?可迭代对象,迭代器,这两个是一样的吗?
迭代器是实现了$__next__$特殊方法的对象,而可迭代对象实现了$__iter__$方法,如果你需要迭代器能够迭代自身,也需要实现$__iter__$方法。要注意的是,可迭代的对象必须实现$__iter__$方法,但不能实现 $__next__$ 方法。
在迭代器中实现了$__next__$方法,你就能使用next(data)来依次产出数据,如果此时没有数据了,就会产生异常。像生孩子一样,next一下生一个。
有点绕是不是,其实,一般只要知道可迭代对象以及它是如何实现的就行了,python 中常常用生成器来代替迭代器,可以说,生成器就是迭代器。因为生成器也实现了$__iter__$和$__next__$方法。
python中还有一个iter函数用来生成迭代器,比如把一个列表放进去,就可以使用next方法来一个个调用了。
说了这么多,来看个例子。
data = [1, 2, 3, 4] # data是列表,是个可迭代对象 # 使用循环迭代 for i, j in enumerate(data): print(i, j) # 生成一个迭代器 d = iter(data) # 调用next next(d) # 调用四次之后就会产生异常
0 1 1 2 2 3 3 4 1
我们来自己实现一个迭代器。
# 从后往前产出列表中的数据 class ReverseList: def __init__(self, item): #用range构造一个列表 self.list = list(range(item)) def __iter__(self): return self def __next__(self): try: return self.list.pop() except: raise StopIteration
用一下试试。
data = ReverseList(4) # 调用next,从后往前产出数据 print(next(data)) print(next(data)) print(next(data)) print(next(data)) # 如果继续调用,会产生错误,因为没有数据可产出了 print(next(data))
3 2 1 0 --------------------------------------------------------------------------- IndexError Traceback (most recent call last)in __next__(self) 12 try: ---> 13 return self.list.pop() 14 except: IndexError: pop from empty list During handling of the above exception, another exception occurred: StopIteration Traceback (most recent call last) in () 7 print(next(data)) 8 # 如果继续调用,会产生错误,因为没有数据可产出了 ----> 9 print(next(data)) in __next__(self) 13 return self.list.pop() 14 except: ---> 15 raise StopIteration StopIteration:
那什么又是生成器?
在程序设计中,内存是个很宝贵的东西,占用太多的内存总是不好的,生成器的作用就是我先把你的数据表示出来,但是实际上并不占用内存空间,只有在你调用它时才会占用。一般情况下,比如你定义了一个列表,会自动的使用一段内存空间。
在 python中,只要定义了yield关键字的函数就是生成器。上面说了,生成器就是迭代器,你可以进行迭代操作(循环),调用next来一个个产出数据。
def gen123(): yield 1 yield 2 yield 3 for i in gen123(): print(i) g = gen123() print(next(g))
1 2 3 1
虽然说生成器就是迭代器,但是在python的定义中,迭代器用来遍历集合,从中产出元素,而生成器无需遍历集合就能生成值,比如range()函数,生成器不仅能够产出集合中的元素,还可以产出派生自元素的其他值。
我在上一篇文章(Python奇遇记:数据结构窥探2)中提到过生成器表达式,只介绍了一下它的用法,那么生成器表达式是什么东西?
其实,生成器表达式就是生成器的快速实现而已,类似于之前讲过的具名元组。有些时候需要快速的生成一段数据,使用生成器表达式即可,无需定义函数再调用。
本人才疏学浅,上文中难免有些错误,还请各位品评指正。如果觉得写的还行,欢迎关注我的公众号MLGroup,带你走进机器学习的世界。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/44531.html
摘要:挤掉了堆中实现了堆排序。你可以用堆排序来查找一个序列中最大的或者最小的几个元素。除了使用堆排序,中还有排序和,这两个排序最终生成以列表表示的排序结果,堆排序也是。 这次我们来说说python中的数据结构。当然了,不会讲很基础的内容。 用过python的都知道,python有着与其他语言很不一样的数据类型,像什么列表、元组、集合、字典之类。这些数据类型造就了python简单易用同时又很强...
摘要:找出列表中小于的数据除了列表推导式,还有字典推导式,集合推导式,用法都一样。如果你的数据量很大的话,考虑使用生成器表达式。切片不仅对列表有用,同样适用于元组和字符串。切片命名使用方法,内部参数与切片一样。对剩余的的数据,使用星号代替即可。 上次我们讲了几个不常见的数据类型,每个都有自己特殊的用途,虽然不经常用到,了解一下也好。比如我们提到的数组类型,如果在数据量很大的时候同时要效率,就...
摘要:在中,特殊方法以双下划线开始,以双下划线结束。真假值,如果向量模为,返回实现向量加法实现向量乘法,例如返回向量的模返回欧几里德范数找个例子运行下。怎么办中有个特殊方法,可以修改控制台输出的样式。 什么是特殊方法?当我们在设计一个类的时候,python中有一个用于初始化的方法$__init__$,类似于java中的构造器,这个就是特殊方法,也叫作魔术方法。简单来说,特殊方法可以给你设计的...
摘要:协程的历史说来话长,要从生成器开始讲起。我们可以使用把数据发送给协程函数。可以看到,在第次接收完数据之后,会产生结束的异常,因为程序流程结束了,这是正常现象。在这个阶段,协程本质上还是由生成器构成的。所以,协程的介绍到这里就结束啦。 在上一篇对python并发编程的理解 中,我简单提到了协程的概念,有一个错误需要指出的是,asyncio不全是对协程的实现,只是用到了协程。 协程的历史说...
摘要:字典和集合都是基于散列表实现的,散列表也就是表,了解过数据结构的应该知道。而使用另一种办法,任何键在找不到的情况下都会用中的值数据类型比如替换。在设计时就可以使用创建你的数据接口。 这次主要说说字典和集合这两种数据类型。 字典和集合都是基于散列表实现的,散列表也就是hash表,了解过数据结构的应该知道。与散列表相关的一个概念叫做可散列,什么是可散列?在python官方定义中是这样说的:...
阅读 853·2023-04-26 00:11
阅读 2652·2021-11-04 16:13
阅读 2101·2021-09-09 09:33
阅读 1470·2021-08-20 09:35
阅读 3816·2021-08-09 13:42
阅读 3603·2019-08-30 15:55
阅读 1037·2019-08-30 15:55
阅读 2218·2019-08-30 13:55