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规则判断,通过判断就不断向上层返回,不通过就再次向最底层遍历。

  • 相关阅读:
    istio-流量管理-基于不同版本访问规则控制
    k8s安装istio
    策略模式
    递归思想
    Java Lambda 表达式
    redis实现分布式锁
    MySQL 的优化方案总结
    linux下查看某一端口被哪个进程占用
    组合模式
    桥接模式
  • 原文地址:https://www.cnblogs.com/majiale/p/10065239.html
Copyright © 2011-2022 走看看