zoukankan      html  css  js  c++  java
  • filter函数与无限生成器结合使用遇到的问题

      python3中用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用list()来转换。 

           廖雪峰关于filter的使用 很好的解释了filter的作用,以及和生成器的结合,但是让人疑惑的是:

    it = filter(_not_divisible(n), it) # 构造新序列

      以上这段代码的具体实现细节是什么?it是个生成器,使用filter对其进行操作的时候,并没有陷入无限循环,难道是对当前已生成的所有数据进行操作?那为什么在9这个数的时候,又能被已经跳过的3给滤除?有可能是filter给原有的it添加了判断规则,在生成9这个数的时候,自动执行filter规则?带着这个疑问,在官网的文章中并没有找到合理的解释。但是发现如下代码可以使用同样的功能,给了更多的思考线索。

    from itertools import filterfalse
    def _odd_iter():
        n = 1
        while True:
            n = n + 2
            yield n
            
    def _not_divisible(n):
        return lambda x: x % n == 0
    
    def primes():
        yield 2
        it = _odd_iter() # 初始序列
        while True:
            n = next(it) # 返回序列的第一个数
            yield n
            it = filterfalse(_not_divisible(n), it) # 构造新序列
    
    # 打印1000以内的素数:
    for n in primes():
        if n < 1000:
            print(n)
        else:
            break

      上述代码依旧能生成所有的素数,而关于filterfalse的源码如下所示:

    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

      这段代码依旧让人觉得费解,为什么filterfalse没有返回值,it感觉确实被赋了值,这个值就行是什么?又做了如下实验:

    a = [1, 2, 3, 4, 5]
    a = iter(a)
    b = filterfalse(lambda x: x%2==0, a)
    print(type(a))
    print(type(b))
    print(next(b))
    print(next(b))
    print(next(b))
    
    输出:
    <class 'list_iterator'>
    <class 'itertools.filterfalse'>
    1
    3
    5

      从中可以看出it是指向了itertools.filterfalse这个类的一个实例,这个类是可迭代的,通过next(b)输出了结果。b是生成器,a也是迭代器,在b的生成器函数里使用迭代器a。

      所以,原始代码中filter重新赋值后的it是新的可迭代的类型,不是原始的生成器。就算是type(_odd_iter)也是<class 'function'>,具有生成器功能的函数。

    结论

      在primes循环while中,每次it会重新赋予一个新的生成器,这个生成器嵌套了之前的生成器,是递归调用,最终的效果正如之前的猜想,相当于添加了规则,每次next的时候,对所有的规则遍历一遍。具体的代码执行是,每一个生成器函数都保存在内存中,一个生成器函数又嵌套了上一个生成器函数,每次next的时候,都会把之前所有的生成器函数递归一遍,等同于执行所有的规则。每执行一次训练就会加一层嵌套,所有每次next的遍历深度都在增加。

      最后发现自己好蠢,it = filterfalse(_not_divisible(n), it) 这行代码就是明显的递归调用。

    代码执行过程

    输出:2

    ①it

    输出:3

    ②it -> ①it

    n=next(it) -> for x in ①iterable(相当于在原有生成器上执行了一次next) -> x=5,(判断能被3整除吗)yield 5  

    n=5

    输出:5

    ③it -> ②it -> ①it

    n=next(it) -> for x in ②iterable -> for x in ①iterable -> x=7,(判断能被3整除吗)yield 7 -> ②iterable -> x=7,(判断能被5整除吗)yield 7

    n=7

    输出:7

    ④ -> ③it -> ②it -> ①it

    n=next(it) -> for x in ③iterable  -> for x in ②iterable -> for x in ①iterable -> x=9 -> 能被3除去(没有进入if,没有执行yield,继续for循环) -> x=11, (判断能被3整除吗)yield 11 -> ②iterable -> x=11, (判断能被5除吗)yield 11 -> ③iteable -> x=11, (判断能被7整除吗)yield 11 

    n=11

    输出:11

    总结

      在递归调用中,yield相当于return,for x in iterable相当于调用嵌套函数。每次产生新的数据都是从最原始_odd_iter()产生,每返回一层,就进行一次if规则判断,通过判断就不断向上层返回,不通过就再次向最底层遍历。

  • 相关阅读:
    BNUOJ 12756 Social Holidaying(二分匹配)
    HDU 1114 Piggy-Bank(完全背包)
    HDU 2844 Coins (多重背包)
    HDU 2602 Bone Collector(01背包)
    HDU 1171 Big Event in HDU(01背包)
    HDU 2571 命运 (入门dp)
    HDU 1069 Monkey and Banana(最长递减子序列)
    HDU 1160 FatMouse's Speed (最长上升子序列)
    HDU 2594 KMP
    POJ 3783 Balls --扔鸡蛋问题 经典DP
  • 原文地址:https://www.cnblogs.com/majiale/p/10065239.html
Copyright © 2011-2022 走看看