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(代理迭代)

首先引入概念:

  1. 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')
>>>