python奇遇记:迭代器和生成器

来说说迭代器和生成器,还有可迭代对象和生成器表达式。

之前简单的提到过,一个对象是可迭代的可以理解为能够使用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)

<ipython-input-30-cfc36c66205d> 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)

<ipython-input-35-1dd792231191> in <module>()
      7 print(next(data))
      8 # 如果继续调用,会产生错误,因为没有数据可产出了
----> 9 print(next(data))


<ipython-input-30-cfc36c66205d> 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,带你走进机器学习的世界。
图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值