可迭代的对象、迭代器和生成器

可迭代的对象、迭代器和生成器

可迭代对象

  • iter函数:检查对象是否实现了__iter__方法,以获取一个迭代器
    如果没有实现__iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器
import re
import reprlib

RE_WORD = re.compile('\w+')

class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)
        
    def __getitem__(self, index):
        return self.words[index]
    
    def __len__(self):
        return len(self.words)
    
    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

__getitem__方法还用于索引和切片

  • 可迭代对象:使用iter内置函数可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。
s = 'ABC'
for char in s:
    print(char)
    
# 如果不用for, 迭代器到头会抛出StopIteration异常
# 而Python语言内部会处理for循环和其他迭代上下文中的StopIteration异常
it = iter(s)
while True:
    try:
        print(next(it))
    except StopIteration:
        del it
        break
  • 标准的迭代器接口有__next__和__iter__两个方法
s3 = Sentence('Pig and Pepper')
it = iter(s3)
it
next(it)
next(it)
next(it)
next(it)
list(it) # 迭代器到头了,
list(iter(s3)) # 重新构建迭代器

迭代器

迭代器是这样的对象:实现了无参数的__next__方法,返回序列中的下一个元素;如果没有元素了,那么抛出StopIteration异常。Python的迭代器还实现了__iter__方法,因此迭代器也可以迭代。

典型的迭代器

# 没有实现__getitem__方法,不支持索引和切片
class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)
        
    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)
    
    def __iter__(self):
        return SentenceIterator(self.words)
    
class SentenceIterator:
    def __init__(self, words):
        self.words = words
        self.index = 0
        
    def __next__(self):
        try:
            word = self.words[self.index]
        except IndexError:
            raise StopIteration()
        self.index += 1
        return word
    
    def __iter__(self):
        return self

Sentence的__iter__方法实例化并返回一个迭代器

SentenceIterator也实现了__iter__方法,也可以迭代,也可以通过issubclass(SentenceInterator, abc.Iterator)测试。

生成器函数

  • 使用生成器函数替换SentenceIterator类
class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)
        
    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)
    
    def __iter__(self):
        """生成器函数"""
        for word in self.words:
            yield word
        return
  • 生成器函数的工作原理

只要Python函数的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。

def gen_123():
    yield 1
    yield 2
    yield 3
    
gen_123 # 函数对象
gen_123() #调用时,返回一个生成器对象
next(g)
next(g)
next(g)
next(g) # 生成器函数执行完毕,抛出StopIterator异常

yield产出值

def gen_AB():
    print('start')
    yield 'A'
    print('continue')
    yield 'B'
    print('end.')
for c in gen_AB():
    print('->', c)

for 循环隐式调用next()函数,且会自动捕获StopIterator异常终止循环

惰性实现Sentence

惰性:next()一次生成一个元素,这样能节省大量内存

class Sentence:
    def __init__(self, text):
        self.text = text
        
    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)
    
    def __iter__(self):
        for match in RE_WORD.finditer(self.text):
            yield match.group()

re.finditer函数是re.findall函数的惰性版本,返回的不是列表,而是一个生成器,按需生成re.MetchObject实例

生成器表达式

  • 生成器表达式可以理解为列表推导的惰性版本:不会迫切的构建列表,而是返回一个生成器,按需惰性生成元素
res1 = [x*3 for x in gen_AB()] # 列表推导,一次生产完全部的元素

res2 = (x*3 for x in gen_AB()) # 生成器表达式返回一个生成器,res2是一个生成器对象

  • 生成器表达式实现Sentence类
class Sentence:
    def __init__(self, text):
        self.text = text
        
    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)
    
    def __iter__(self):
        return (match.group() for match in RE_WORD.finditer(self.text))

测试

s = Sentence('"The time has come," the walrus said,')
print(s)
for word in s:
    print(word)
list(s)

等差数列生成器

  • 生成器类实现
class ArithmeticProgression:
    def __init__(self, begin, step, end=None):
        self.begin = begin
        self.step = step
        self.end = end
        
    def __iter__(self):
        result = type(self.begin + self.step)(self.begin)
        forever = self.end is None
        index = 0
        while forever or result < self.end:
            yield result
            index += 1
            result = self.begin + self.step * index
            
ap = ArithmeticProgression(0, 1, 3)
print(list(ap))
ap =ArithmeticProgression(1, .5, 3)
print(list(ap))
ap = ArithmeticProgression(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = ArithmeticProgression(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = ArithmeticProgression(0, Decimal('.1'), .3)
print(list(ap))
  • 生成器函数替换生成器类
def aritprog_gen(begin, step, end=None):
    result = type(begin + step)(begin)
    forever = end is None
    index = 0
    while forever or result < end:
        yield result
        index += 1
        result = begin + step * index
  • 使用itertools模块生成等差数列
import itertools
gen = itertools.count(1, .5)
next(gen), next(gen), next(gen),next(gen) # gen会一直产出,不会停止

# 使用itertools.takewhile停止count
gen = itertools.takewhile(lambda n:n < 3, itertools.count(1, .5))
list(gen)
  • 使用itertools.count和itertools.takewhile替换生成器函数
import itertools

def ariteprog_gen(begin, step, end=None):
    first = type(begin + step)(begin)
    ap_gen = itertools.count(first, step)
    if end is not None:
        ap_gen = itertools.takewhile(lambda n:n < end, ap_gen)
    return ap_gen

标准库中的生成器函数

用于过滤的生成器函数

def vowel(c):
    return c.lower() in 'aeiou'

list(filter(vowel, 'Aardvark')) # 把’Aardvark’各个元素传给vowel,vowel返回真值,则产出对应元素

list(itertools.filterfalse(vowel, 'Aardvark')) # 与filter相反

list(itertools.dropwhile(vowel, 'Aardvark')) # 跳过owel返回为真的值,一直到返回假值时则产出剩下的所有元素

list(itertools.takewhiel(vowel, 'Aardvark')) # vowel返回真值时产出对应的元素,遇到假值时则立即停止

list(itertools.compress('Aardvark', (1,0,1,1,0,1))) # 并行迭代两个对象,第二个参数对象中的值为真时则产出‘Aardvark’中对应的元素

list(itertools.silice('Aardvark', 4)) # 惰性操作’Aardvark’的切片
list(itertools.silice('Aardvark', 4, 7))
list(itertools.silice('Aardvark', 1, 7, 2))

用于映射的生成器函数

sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
import itertools
list(itertools.accumulate(sample)) # 计算sample总和

list(itertools.accumulate(sample, min)) # 计算sample最小值

list(itertools.accumulate(sample, max)) # 计算sample最大值

import operator
list(itertools.accumulate(sample, operator.mul)) # 计算乘积

list(itertools.accumulate(range(1, 11), operator.mul)) # 计算1!到10!各个数的阶乘

list(enumerate('albatroz', 1)) # 编号‘albatroz’

list(map(operator.mul, range(11), range(11))) # 1 到 10 各个数的平方

list(map(operator.mul, range(11), [2, 4, 8])) # 对应位置上的积
 
list(map(lambda a, b: (a, b), range(11), [2, 4, 8])) # 对应位置上的编号

import itertools
list(itertools.starmap(operator.mul, enumerate('albatroz', 1))) # 对应位置上的字母重复

list(itertools.starmap(lambda a, b: b/a, enumerate(itertools.accumulate(sample), 1))) # 计算平均值

合并多个可迭代对象的生成器函数

list(itertools.chain('ABC', range(2))) # 无缝连接两个可迭代对象

list(itertools.chain.from_iterable(enumerate('ABC'))) # 按顺序连接可迭代对象的元素

list(zip('ABC', range(5))) # 把两个可迭代的对象合并成元组

list(itertools.zip_longest('ABC', range(5))) # 使用fillvalue填充到头的可迭代对象

# itertools.product
list(itertools.product('ABC', range(2))) # 生成笛卡尔积

suits = 'spades hearts diamonds clubs'.split()
list(itertools.product('AK', suits)) 

list(itertools.product('ABC', repeat=2)) # 可以重复2次处理可迭代对象的输入
list(itertools.product(range(2), repeat=3))
rows = itertools.product('AB', range(2), repeat=2))
for row in rows: print(row)

把各个输入的元素扩展成多个输出元素的生成器函数

ct = itertools.count() # 构建生成器
next(ct), next(ct), next(ct)

list(itertools.islice(itertools.count(1, .3), 3))

cy = itertools.cycle('ABC') # 构建cycle生成器
next(cy)

list(itertools.islice(cy, 7))

rp = itertools.repeat(7) # 构建重复的生成器
next(rp), next(rp)
list(itertools.repeat(8, 4))

list(map(operator.mul, range(11), itertools.repeat(5)))

组合学 生成器函数

list(itertools.combinations('ABC', 2)) # ‘ABC’中两个元素的各种组合

list(itertools.combinations_with_replacement('ABC', 2)) # 包括相同元素的组合

list(itertools.permutations('ABC', 2)) # ‘ABC’中两个元素的排列

list(itertools.product('ABC', repeat=2)) # ‘ABC’和‘ABC’的笛卡尔积

用于重新排列元素的生成器函数

list(itertools.groupby('LLLLAAGGG'))
for char, group in itertools.groupby('LLLLAAAGG'):
    print(char, '-->', list(group))
    
animals = ['duck', 'eagle', 'rat', 'giraffe', 'bear', 'bat', 'dolphin', 'shark', 'lion']
animals.sort(key=len)
for length, group in itertools.groupby(animals,len):
    print(length, '->', list(group))
    
for length, group in itertools.groupby(reversed(animals), len):
    print(length, '->', list(group))
    
list(itertools.tee('ABC'))
g1, g2 = itertools.tee('ABC') # 从输入的可迭代对象中产出多个生成器
next(g1)
next(g2)
next(g2)
list(g1)
list(g2)
list(zip(*itertools.tee('ABC')))

yield from

def chain(*iterables):
    for it in iterables:
        for i in it:
            yield 
s = 'ABC'
t = tuple(range(3))
list(chain(s, t))

# 使用yield from 替换
def chain(*iterables):
    for i in iterables:
        yield from i
        
list(chain(s, t))

可迭代的归约函数

接受一个可迭代的对象,返回单个结果

all([1, 2, 3]) # 全为真是返回True
all([1, 0, 3])
all([]) # 返回True

any([1, 0, 3]) # 相当与or,有真值返回True
any([0, 0.0]) # 全为假时,返回False

深入分析iter函数

  • iter函数传入两个参数

第一个参数必须是可调用的对象,用于不断调用,产出各个值;第二个值是哨符,这是个标记,当可调用的对象返回这个值时,触发迭代器抛出StopIteration异常,而不产出哨符

from random import randint
def d6():
    return randint(1, 6)

d6_iter = iter(d6, 1)
d6_iter
for roll in d6_iter:
    print(roll)

用生成器实现斐波那契数列

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b
next(f), next(f), next(f)

总结

  • 可迭代对象实现的__iter__方法
  • 标准迭代器接口的__next__和__iter__方法
  • 从典型的迭代器,到生成器函数,再到生成器表达式,一步步的优化Sentence
  • 使用生成器实现等差数列
  • 标准库itertools中的生成器函数的示例

流畅的Python

上一篇:【python日用】itertools.product用法


下一篇:python学习记录(四)-意想不到