python iterator实现_一文了解Python的迭代器的实现

本文对迭代器的解释参考自:https://www.programiz.com/python-programming/iterator

最后自己使用迭代器实现一个公平洗牌类。

博主认为,理论来自实践,假若只学习理论而不实践,都是无用功。

Iterators in Python

迭代器在Python中无处不在。它们可以通过for循环优雅的使用。但是它的实现却被隐藏起来。

从技术上讲,Python迭代器对象必须实现两个特殊的方法,分别是__iter__() 和 __next__(),这两个方法也叫做Python的魔术方法,类似于一种迭代器协议。

如果我们可以将Python对象转换成一个迭代器,那么我们可以称这个对象是可以迭代的。像Python中的内置数据结构 list(列表)、tuple(元组)、string(字符串)等都是可迭代的。

注意:这里可迭代与迭代器是不同的概念,下面会讲到。

遍历迭代器

我们可以通过next()方法不断从迭代器中获取下一个元素。当迭代器中元素遍历完毕后,再次调用next()方法,迭代器会抛出StopIteration异常。下面是例子。

#:创建一个列表。

>>> test_list = [5, 4, 3, 2, 1, 0]#:使用iter()将列表转换成迭代器。

>>> test_iter =iter(test_list)#:使用next方法我们可以得到迭代器中的元素。

>>> print(next(test_iter))5

>>> print(next(test_iter))4

>>> print(next(test_iter))3

>>> print(next(test_iter))2

#:我们可以调用迭代器的魔法方法__next__获取下一个元素。

>>> print(test_iter.__next__())1

>>>next(test_iter)

0#:当迭代器中元素遍历完毕,再调用next()时迭代器抛出错误。

>>>next(test_iter)

Traceback (most recent call last):

File"", line 1, in StopIteration

注意,这里我们如果不进行iter()操作的话,列表是否还支持next()等操作?我们看下面实际操作。

>>> test_list = [5, 4, 3, 2, 1, 0]>>>next(test_list)

Traceback (most recent call last):

File"", line 1, in TypeError:'list' object is not an iterator

没错,程序发生报错,并且报错原因就是list并不是一个迭代器。

我们前面也强调过可迭代与迭代器并不是同一个概念,我们可以说list是可迭代的,但并不能说它是迭代器。

读到这里大家可能会疑问,平常使用for循环便利list的时候也没有主动将其变成迭代器操作的,别急,我们接着看下面的。

用于迭代器的for循环

>>> test_list = [5, 4, 3, 2, 1, 0]>>> for i intest_list:

...print(i)

...5

4

3

2

10

上面这个使用for循环遍历列表的例子也屡见不鲜了。实际上,for循环可以遍历任何可迭代对象。下面我们来看看for循环的实现。

iter_obj =iter(iterable)whileTrue:try:

element=next(iter_obj)exceptStopIteration:break

因此,for 循环在内部通过iter()方法产生一个迭代器对象。接着使用next()方法依次获取迭代器内部元素,直到抛出异常为止。

构建自定义的迭代器

前面我们也提到过对象中的__iter__()和__next__()方法。通过更改这两个魔法方法我们可以很轻易实现一个自定义的迭代器。

__iter__()返回一个迭代器对象,当然我们也可以在当中根据需要进行一些初始化操作。

__next__()返回下一项,此方法在调用到结尾时必须抛出StopIteration()。

下面这个例子实现要返回2的幂次方的迭代器对象。

classPowTwo:def __init__(self, max=0):

self.max=maxdef __iter__(self):#:返回一个迭代器,可以是自己。

self.n =0returnselfdef __next__(self):#:判断是否结束遍历。

if self.n <=self.max:

result= 2 **self.n

self.n+= 1

returnresultelse:raiseStopIteration

numbers= PowTwo(3)#:获取迭代器。

i =iter(numbers)print(next(i))print(next(i))print(next(i))print(next(i))print(next(i))

# :输出

1

2

4

8

Traceback (most recent call last):

File "/home/bsoyuj/Desktop/Untitled-1.py", line 32, in

print(next(i))

File "", line 18, in __next__

raise StopIteration

StopIteration

当然我们也可以使用for循环。

>>> for i in PowTwo(5):

...print(i)

...1

2

4

8

16

32

注意

最后注意的是,迭代器要有尽头,类似于递归,迭代器也要有迭代结束条件来防止迭代器会无限迭代。

Knuth洗牌示例

这里结合Knuth洗牌算法实现一个洗牌类。此算法为知名的公平洗牌算法。此算法详细链接。

importrandom

COLORS= ['红桃', '黑桃', '方片', '梅花']classKnuth:"""Kunuth洗牌算法"""

def __init__(self):

self._pokers=[]for color inCOLORS:for i in range(1, 14):

self._pokers.append((i, color))

self._pokers.append(('大王'))

self._pokers.append(('小王'))#:记录扑克牌索引。

self._index =0#:一套扑克最多有54张牌。

self._max_index = 54

def __iter__(self):returnselfdef __next__(self):if self._index

card=self._pokers[self._index]

self._index+= 1

returncardelse:raiseStopIterationdefshuffle_cards(self):"""洗牌"""

for i in range(53, 0, -1):

swap_index=random.randint(0, i)

self._pokers[i], self._pokers[swap_index]=self._pokers[swap_index], self._pokers[i]#:计数索引归零。

self._index =0

a=Knuth()

a.shuffle_cards()for i ina:print(i)

运行结果。

(2, '梅花')

(13, '红桃')

(3, '黑桃')

(6, '方片')

(5, '红桃')

大王

(3, '红桃')

(8, '方片')

(4, '黑桃')

(9, '方片')

(1, '红桃')

(10, '红桃')

(6, '梅花')

(8, '梅花')

...

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值