本文对迭代器的解释参考自: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 "<stdin>", line 1, in <module>
StopIteration

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

>>> test_list = [5, 4, 3, 2, 1, 0]
>>> next(test_list)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

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

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

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

用于迭代器的for循环

>>> test_list = [5, 4, 3, 2, 1, 0]
>>> for i in test_list:
... print(i)
...
5
4
3
2
1
0

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

iter_obj = iter(iterable)

while True:
try:
element = next(iter_obj)
except StopIteration:
break

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

构建自定义的迭代器

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

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

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

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

class PowTwo:

    def __init__(self, max=0):
self.max = max def __iter__(self):
# :返回一个迭代器,可以是自己。
self.n = 0
return self def __next__(self):
# :判断是否结束遍历。
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration 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 <module>
print(next(i))
File "<string>", line 18, in __next__
raise StopIteration
StopIteration

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

>>> for i in PowTwo(5):
... print(i)
...
1
2
4
8
16
32

注意

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

Knuth洗牌示例

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

  

import random

COLORS = ['红桃', '黑桃', '方片', '梅花']

class Knuth:
"""Kunuth洗牌算法""" def __init__(self):
self._pokers = []
for color in COLORS:
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):
return self def __next__(self):
if self._index < self._max_index:
card = self._pokers[self._index]
self._index += 1
return card
else:
raise StopIteration def shuffle_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 in a:
print(i)

  运行结果。

(2, '梅花')
(13, '红桃')
(3, '黑桃')
(6, '方片')
(5, '红桃')
大王
(3, '红桃')
(8, '方片')
(4, '黑桃')
(9, '方片')
(1, '红桃')
(10, '红桃')
(6, '梅花')
(8, '梅花')
...

  

最新文章

  1. Jquery中的(function($){...})(jQuery)
  2. 精通Web Analytics 2.0 (7) 第五章:荣耀之钥:度量成功
  3. composer [ReflectionException] Class Fxp\Composer\AssetPlugin\Repository\NpmRepository does not exist
  4. nginx+iis、NLB、Web Farm、Web Garden、ARR
  5. [51单片机] 以PWM控制直流电机为例建一个简单的51工程框架
  6. JavaScript Patterns 5.6 Static Members
  7. 【HTML5】Canvas和SVG的区别
  8. Altium Designer 6三维元件库建模教程
  9. leetcode 5 :Longest Palindromic Substring 找出最长回文子串
  10. BZOJ 1968: [Ahoi2005]COMMON 约数研究 水题
  11. js实现过滤重复字符和重复数组-javascript技巧
  12. web.py安装
  13. jmeter连接配置带跳板机(SSH)的mysql服务器
  14. fgets的用法
  15. 【一天一道LeetCode】索引目录 ---C++实现
  16. 单片机开发——03工欲善其事必先利其器(AD软件安装破解)
  17. python3 练手实例3 摄氏温度与华氏温度转换
  18. 【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)
  19. 【做题】cf603E——线段树分治
  20. koa中间件机制

热门文章

  1. SPOJ - PHRASES Relevant Phrases of Annihilation
  2. 牛客编程巅峰赛S1第11场 - 黄金&amp;钻石 B.新集合 (DFS)
  3. Codeforces Round #654 (Div. 2) C. A Cookie for You (思维)
  4. k8s二进制部署 - traefik安装
  5. OpenStack Train版-1.安装基础环境&amp;服务
  6. zoj-3870 (二进制)
  7. Bootstrap巨幕
  8. 2021-2-17:Java HashMap 的中 key 的哈希值是如何计算的,为何这么计算?
  9. Ant-design-vue—— 表单输入框输入很卡问题
  10. soft tab