python魔法之迭代器
2021/8/9(迭代器初始next())
import datetime
# 迭代器
List_example = ["A", 1, lambda x: x+2]
it = iter(List_example)
it_1 = next(it)
it_2 = next(it)
it_3 = next(it)
it_3_example = it_3(2)
print("it_1:", it_1,"\n", "it_2:", it_2,"\n", "it_3:",it_3_example)
try:
it_4 = next(it)
except StopIteration:
print("It is end, assert StopIteration")
pass
# 字符串也可以进行迭代
A = "abcde"
start = datetime.datetime.now()
B = iter(A)
# C = next(B)
D = [next(B) for i in range(len(A))]
E = [x for x in A]
print(E)
print(D)
运行结果
it_1: A
it_2: 1
it_3: 4
It is end, assert StopIteration
['a', 'b', 'c', 'd', 'e']
['a', 'b', 'c', 'd', 'e']
2021/8/10(代理迭代)
首先引入概念:
- Iterable: 有迭代能力的对象,一个类,实现了__iter__, 那么认为它有迭代能力,通常该函数必须返回一个实现了__next__的对象,如果自己实现了,那么__iter__,函数可以返回self,这样这个类就变成了迭代器(Iterator)
实验
首先创建一个只包含__iter__的类
class Node:
def __init__(self, value):
self._value = value
self._children = []
def __repr__(self):
return '显示属性: Node({!r})'.format(self._value)
def add_child(self, node):
self._children.append(node)
def __iter__(self):
# 当对象进行迭代操作时,执行该方法
return iter(self._children)
if __name__ == '__main__':
root = Node(0)
print(isinstance(root, Iterable))
print(isinstance(root, Iterator))
结果:
说明Node类是个可迭代对象,但是不是迭代器
True
False
from collections.abc import *
class Node:
def __init__(self, value):
self._value = value
self._children = []
def __repr__(self):
return '显示属性: Node({!r})'.format(self._value)
def add_child(self, node):
self._children.append(node)
def __iter__(self):
# 当对象进行迭代操作时,执行该方法
return self
def __next__(self):
print(1)
if __name__ == '__main__':
root = Node(0)
print(isinstance(root, Iterable))
print(isinstance(root, Iterator))
结果:
在类中加入__next__函数后,这个类变成了一个迭代器,迭代器的优点是能够节约内存,相比于列表来说,迭代器每迭代一次生成一个数。
True
True
2021/8/11(使用生成器创建新的迭代模式)
相比于需要创建__iter__和__next__的迭代器类要方便,但是如果需要写入较为复杂的函数则显得不是很pythonic
实验
def frange(start, stop ,increment):
x = start
while x < stop:
yield x
x+=increment
for n in frange(0, 4, 0.5):
print(n)
结果
0
0.5
1.0
1.5
2.0
2.5
3.0
3.5
可以得出,frange已经成为一个可迭代对象,并且每次的返回值是yield后面的值
def countdown(n):
print('Starting to count from', n)
while n > 0:
yield n
n -= 1
print('Done!')
c = countdown(3)
for i in c:
print(i)
结果:
Starting to count from 3
3
2
1
Done!
可以得到,在首次进行迭代时,会执行函数中循环之前的所有内容,开始迭代后,则只执行循环内的内容,结束迭代时,执行循环外的内容。
2021/8/12(反向迭代)
如果我们想反向迭代一个序列,那么我们想反转它,可以用内置的reversed()函数。
反向迭代仅仅当对象的大小可预先确定或者对象实现了__reversed__()的特殊方法才能生效。如果两者都不符合,那必须先将对象转换为一个列表才行
例子
class Countdown:
def __init__(self, start):
self.start = start
# Forward iterator
def __iter__(self):
n = self.start
while n > 0:
yield n
n -= 1
# Reverse iterator
def __reversed__(self):
n = 1
while n <= self.start:
yield n
n += 1
for rr in reversed(Countdown(30)):
print(rr)
for rr in Countdown(30):
print(rr)
定义一个反向迭代器可以使得代码非常高效,因为它不再需要将数据填充到一个列表中然后再反向迭代这个列表。
2021/8/13(迭代切片)
在想对迭代器进行切片时,不能使用简单的切片,必须使用itertools.islice()进行切片
import itertools
for x in itertools.islice(c,10,20):
print(x)
但是值得注意的是,该操作会消耗传入迭代器的数据,即迭代器不可逆,因此需要将x传入列表进行保存。
2021/8/14(排列组合的迭代)
如果我们想计算一个列表的排列组合,那么我们可以使用itertools中的permutations(排列),combinations(组合)
from itertools import permutations
from itertools import combinations
items = ['a', 'b', 'c']
for p in permutations(items, 3):
print(p)
for c in combinations(items, 2):
print(c)
结果
# 排列
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')
# 组合
('a', 'b')
('a', 'c')
('b', 'c')
关于zip
zip(a,b) 会生成一个可返回元祖(x,y)的迭代器,其中x来自a,y来自b。一旦其中某个序列到底结尾,迭代宣告结束。因此迭代长度跟参数中最短序列长度一致。
如果我们需要统一长度,我们可以使用itertools.zip_longest()
>>> from itertools import zip_longest
>>> for i in zip_longest(a,b):
... print(i)
...
(1, 'w')
(2, 'x')
(3, 'y')
(None, 'z')
>>> for i in zip_longest(a, b, fillvalue=0):
... print(i)
...
(1, 'w')
(2, 'x')
(3, 'y')
(0, 'z')
>>>