分享

读itertools源码,学会耶熬的yield

 大邓的Python 2021-02-23

itertools

想必大家也很困惑yield功用,这次可以阅读内置的itertools库中的函数实现方式,了解yield用法。并且在这过程中熟悉itertools库常用的一些用法。

itertools库可以生产各种迭代器,让迭代更加的高效。

一、 “无限”迭代器

无限序列在for迭代时才会无限地迭代下去,如果只是创建了一个迭代对象,它不会事先把无限个元素生成出来。

1.1 count(start,step)

生成一个序列,开头start,步长为step的迭代对象。

看源码知道count会无限运行下去。

def count(start=0, step=1):    # count(10) --> 10 11 12 13 14 ...    # count(2.5, 0.5) -> 2.5 3.0 3.5 ...    n = start    
   while True:        yield n        n += step

count()会创建一个无限的迭代器,上述代码会打印出根本停不下来的自然数序列,只能按Ctrl+C退出。

试试这个函数

import itertools
#无限循环器,无限制的话根本停不下来
infinite_iter = itertools.count(start=0, step=1)
for x in infinite_iter:    print(x)
... 1 2 3 4 5 6 ...

1.2 cycle()

cycle()会把传入的一个序列无限重复下去

def cycle(iterable):    # cycle('ABCD') --> A B C D A B C D A B C D ...    saved = []    #迭代最初的元素    for element in iterable:        yield element        saved.append(element)        #执行完该for循环后,为了继续循环往复下,执行下面的while语句    while saved:        for element in saved:            yield element

试试这个函数

#字符串也是序列的一种abc = itertools.cycle('ABC')
for x in abc:    print(x)
... 'A' 'B' 'C' 'A' 'B' 'C' ...

1.3 repeat()

repeat(element,repeat_num) 

负责把一个元素element无限重复下去,如果提供第二个参数repeat_num就可以限定重复次数

def repeat(object, times=None):    # repeat(10, 3) --> 10 10 10    if times is None:        while True:            yield object    
   else:        for i in range(times):            yield object

试试这个函数

#将a重复10次
repetition = itertools.repeat('a', 10)
for n in repetition :    print(n)
   a    a    a    a    a    a    a    a    a    a

总结:

无限序列虽然可以无限迭代下去,但是通常我们会通过takewhile()等函数根据条件判断来截取出一个有限的序列

itertools.takewhile(predicate, iterable) 返回满足predicate条件的迭代器中的元素。

二、操作价值极高的几个函数

2.1 chain()

chain() 将括号内的所有可迭代对象,共同顺序输出至一个迭代对象。

看英文chain,链条,相当于是把多个可迭代对象拼接起来。

def chain(*iterables):    # chain('ABC', 'DEF') --> A B C D E F    for it in iterables:        for element in it:            yield element

试试这个函数

for x in itertools.chain([['a','b'], ['c','d']], 'DEF'):    print(x)['a', 'b'] ['c', 'd'] D E F

2.2groupby()

groupby()把迭代器中相邻的重复元素挑出来放在一起,分成不同的部分。

groups = itertools.groupby('AAAABBBCCDAABBB')
for key,group in groups:    print(key, list(group))
A ['A', 'A', 'A', 'A'] B ['B', 'B', 'B'] C ['C', 'C'] D ['D'] A ['A', 'A'] B ['B', 'B', 'B']

groupby的挑选规则是通过函数完成的,只要作用于函数的两个元素返回的值相等,这两个元素就被认为是在一组的,而函数返回值作为组的key。

#看这里,如果groupby()中传入key参数,就可以设置挑选规则。比如大小写都是一样的

class groupby:    #[k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B    #[list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D    def __init__(self, iterable, key=None):        if key is None:            key = lambda x: x        self.keyfunc = key        self.it = iter(iterable)        self.tgtkey = self.currkey = self.currvalue = object()    def __iter__(self):        return self    def __next__(self):        while self.currkey == self.tgtkey:            self.currvalue = next(self.it) # Exit on StopIteration            self.currkey = self.keyfunc(self.currvalue)        self.tgtkey = self.currkey        
       return (self.currkey, self._grouper(self.tgtkey))    def _grouper(self, tgtkey):        while self.currkey == tgtkey:            yield self.currvalue            
           try:                self.currvalue = next(self.it)            except StopIteration:                return            self.currkey = self.keyfunc(self.currvalue)

如果我们要忽略大小写分组,就可以让元素'A'和'a'都返回相同的key

for key, group in itertools.groupby('AaaBBbcCAAa', key=lambda c: c.lower()):    print(key, list(group))a ['A', 'a', 'a'] b ['B', 'B', 'b'] c ['c', 'C'] a ['A', 'A', 'a']

2.3 accumulate(iterable, func=operator.add)

默认func为累加,具体可看下面运行找规律。

def accumulate(iterable, func=operator.add):    'Return running totals'    # accumulate([1,2,3,4,5]) --> 1 3 6 10 15    # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120    it = iter(iterable)    try:        total = next(it)    except StopIteration:        #迭代对象迭代完退出该函数        return    #yield的第一个和    yield total    
   for element in it:        total = func(total, element)        #yield出total后,继续作为下一次迭代的传入的值        yield total

试试这个函数

for x in itertools.accumulate([1,2,3,4,5]):    print(x)1 3 6 10 15

2.4 chain.from_iterable()

智能传入一个可迭代对象作为参数,并将最多两层嵌套关系摧毁。

def from_iterable(iterables):    #chain.from_iterable(['ABC', 'DEF']) --> A B C D E F    for it in iterables:        for element in it:            yield element

如下,列表嵌套列表

#将[['A','B'],'C']所有元素迭代出来
for x in itertools.chain.from_iterable([['A','B'],'C']):    print(x)  
['a', 'b'] ['c', 'd'] C

2.5 compress(data,selector)

data与selector均为可迭代对象,且长度相同。通过selector参数来挑选data中的数据。

selector参数为0或非0。

如果为0,则不返回data中对应的值;

如果为非0,返回data中对应的值。

def compress(data, selectors):    # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F    return (d for d, s in zip(data, selectors) if s)

试试这个函数

for x in itertools.compress('ABCDEF',[1,0,1,0,1,1]):    print(x)A C E Ffor x in itertools.compress('ABCDEF',[-1,0,3,0,1,1]):    print(x)A C E F

2.6 dropwhile(pred, iterable)

返回剔除iterable中满足predicate条件的元素后序列。

def dropwhile(predicate, iterable):    # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1    iterable = iter(iterable)    for x in iterable:        if not predicate(x):            yield x            
           break #yield输出x后break,结束本次    for x in iterable:        yield x
#剔除掉[1,4,6,4,1]中小于5的元素
for x in itertools.dropwhile(lambda x: x<5, [1,4,6,4,1]):    print(x)

print(list(itertools.dropwhile(lambda x: x<5, [1,4,6,4,1])))
6 4 1 [6, 4, 1]

2.7 filterfalse()

filterfalse(predicate, iterable)

保留iterable中满足predicate条件的元素。

def filterfalse(predicate, iterable):    # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8    if predicate is None:        predicate = bool    
   for x in iterable:        if not predicate(x):            yield x
#保留[0,9)中能被2整除的数
for x in itertools.filterfalse(lambda x: x%2, range(10)):    print(x)
0 2 4 6 8

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多