【问题标题】:filter map vs list comprehension过滤映射与列表理解
【发布时间】:2011-06-19 14:07:18
【问题描述】:

过滤器/映射是否等同于列表理解? 假设我有以下功能

def fib_gen():
    a,b = 0,1
    yield 0
    yield 1
    while True:
        a,b = b,a+b
        yield b

现在我可以使用列表推导来列出 fib 数字:

a = fib_gen()
print [a.next() for i in range(int(sys.argv[1]))]

假设我只想列出偶数 fib 数字。我会用过滤器/地图做以下事情:

a = fib_gen()
print filter(even, map(lambda x: a.next(), range(int(sys.argv[1]))))

如何通过列表理解获得相同的结果?

【问题讨论】:

    标签: python


    【解决方案1】:

    过滤器/映射是否等同于列表理解?

    是的,map(f, L) 等同于 [f(x) for x in L]。 filter(f, L) 等价于 [x for x in L if f(x)]。但是,由于具有副作用的列表推导通常很糟糕(在这里你修改了生成器的状态),你可以使用itertools 来获得更简洁的解决方案:

     a = fib_gen()
     a = itertools.islice(a, int(sys.argv[1]))
     a = itertools.ifilter(even, a)
     print list(a)
    

    【讨论】:

      【解决方案2】:

      您可以使用生成器来存储中间结果,并对其进行“过滤”。

      fibs = (a.next() for i in whatever)
      even_fibs = [num for num in fibs if num % 2 == 0]
      

      或一行:

      even_fibs = [num for num in (a.next() for i in whatever) if num % 2 == 0]
      

      请注意,如果您想从迭代器中获取确定数量的元素,您可以改用itertools.islice:

      from itertools import islice
      fibs_max_count = int(sys.argv[1])
      even_fibs = [num for num in islice(fib_gen(), fibs_max_count) if num%2 == 0]
      

      【讨论】:

        【解决方案3】:

        filter 和 map 很容易转换为列表理解。

        这是一个基本的例子:

        [hex(n) for n in range(0, 100) if n > 20]
        

        这相当于:

        list(map(hex, filter(lambda x: x > 20, range(0, 100))))
        

        在我看来,理解更具可读性。但是如果条件变得非常先进,我更喜欢filter。

        所以在你的情况下:

        [n for n in itertools.islice(fib_gen(), 100) if even(n)]
        

        我在这里使用了islice,因为序列是无限的。但如果你使用生成器表达式,它也会变成无限流:

        gen = (n for n in fib_gen() if even(n))
        

        现在您也可以使用islice 对序列进行切片:

        print itertools.islice(gen, int(sys.argv[1]))
        

        这避免了在理解本身中使用next 的需要。只要您不尝试评估无限序列(就像我们在列表理解中省略 islice 一样),我们就可以使用您的序列。

        【讨论】:

        • 谢谢。在我的示例中过滤包含 next() 的表达式怎么样?
        • @Oleg:哦,我正在添加一些信息,查看我最新添加的内容,您会发现您也可以避免使用next。
        • 您的十六进制/范围示例不是传统的过滤器映射。过滤器映射可以过滤映射过程的结果,而不仅仅是该表达式的输入。
        【解决方案4】:

        我不认为这将与生成器一起使用。为了使用列表理解进行这项工作,您需要:

        print [a.next() for i in range(int(sys.argv[1])) if even(a.next())]
        

        这会返回:

        [1, 3, 13, 55, 233]
        

        问题是您需要访问列表中下一个数字的两倍,但第二个 a.next() 调用完成了它应该做的事情,即获取下一个数字。据我所知,不可能使用列表解析存储 a.next() 的值以使用两次。

        【讨论】:

        • 这是可能的,但你必须像print [n for n,i in ( (a.next(),i) for i in range(int(sys.argv[1])) ) if even(n)]一样嵌套,n是a.next()并且被使用了两次。
        【解决方案5】:

        您可以使用以下代码:

        a = fib_gen()
        print [a.next() for i in range(int(sys.argv[1])) if i%3==0]
        

        这是一个有效的特殊情况,因为每三个斐波那契数都是偶数。

        【讨论】:

          【解决方案6】:

          您也总是可以生成偶数斐波那契数...

          def evenfib():
              """ Generates the even fibonacci numbers """
              a, b = 2, 0
              while True:
                  a, b = b, a+4*b
                  yield a
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-02-22
            • 2019-08-20
            • 1970-01-01
            • 2011-03-02
            • 2011-02-28
            • 1970-01-01
            相关资源
            最近更新 更多