【问题标题】:Avoiding multiple calls to the same function in a list comprehension [duplicate]避免在列表理解中多次调用同一函数[重复]
【发布时间】:2020-06-23 15:54:58
【问题描述】:

我有一个返回如下元组的函数:

def foo(i,j):
    # does stuff
    return (m,n)

我想对两个输入进行双重列表理解,并获得返回元组中两个元素之和的值。我能想到的最简单的方法是:

[foo(i,j)[0] + foo(i,j)[1] for i in x for j in y]

这种方法的问题是foo 每对i,j 被调用两次,使得列表理解比必要的慢。如何更改列表理解,以便每对 i,j 只调用一次 foo

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    您可以在 Python 3.8+ 中使用Walrus operator

    [p[0] + p[1] for i in x for j in y if (p := foo(i,j))]
    

    需要 if 条件才能使其成为双 for 循环的有效用法(单 for 循环不需要)。但是,如果总是 True 因为 foo 返回一个元组。

    更好的版本(由@MisterMiyagi 提供)

    [(p:=foo(i, j))[0] + p[1] for i in x for j in y]
    

    这需要 if 条件。

    【讨论】:

    • 不错 :-) 不熟悉这个
    • 接受这个答案,因为它是最通用的
    • @bleutooth65--是的,Walrus 是 3.8+ 的一个很棒的功能。
    • 不需要危险的if 子句!请改用[(p:=foo(i, j)[0] + p[1] for i in x for j in y]
    • @MisterMiyagi——你的表达有误吗?我按原样使用时出现语法错误:p := foo(i,j)] ^ SyntaxError: invalid syntax
    【解决方案2】:

    试试这个:

    [sum(foo(i,j)) for i in x for j in y]
    

    【讨论】:

      【解决方案3】:

      试试Python内置的sum()函数:

      [sum(foo(i,j)) for i in x for j in y]
      

      样本运行:

      def foo(m,n):
          print(f'called: foo({m},{n})')
          return m,n
      
      x = [1,2]
      y = [3,4]
      
      print('Run without sum() - expect 8 calls')
      [foo(i,j)[0] + foo(i,j)[1] for i in x for j in y]
      
      print('\nRun with sum() - expect 4 calls')
      [sum(foo(i,j)) for i in x for j in y]
      
      > Run without sum() - expect 8 calls
      > called: foo(1,3)
      > called: foo(1,3) 
      > called: foo(1,4) 
      > called: foo(1,4)
      > called: foo(2,3) 
      > called: foo(2,3) 
      > called: foo(2,4) 
      > called: foo(2,4)
      > [4, 5, 5, 6]
      >
      > Run with sum() - expect 4 calls
      > called: foo(1,3)
      > called: foo(1,4)
      > called: foo(2,3)
      > called: foo(2,4)
      > [4, 5, 5, 6]
      

      【讨论】:

        【解决方案4】:

        operator 模块提供所有运算符作为一流的函数。 operator.add(a, b) 等价于a + boperator.add(*ab) 等价于operator.add(ab[0], ab[1])

        from operator import add
        [add(*foo(i,j)) for i in x for j in y]
        

        使用addsum 稍快:

        In [1]: %timeit [add(*foo(i, j)) for i in range(50) for j in range(50)]
        409 µs ± 8.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        In [2]: %timeit [sum(foo(i, j)) for i in range(50) for j in range(50)]
        554 µs ± 5.97 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        In [3]: %timeit [add(*foo(i, j)) for i in range(500) for j in range(500)]
        47.2 ms ± 215 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
        In [4]: %timeit [sum(foo(i, j)) for i in range(500) for j in range(500)]
        61.5 ms ± 215 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-24
          • 2017-04-27
          • 1970-01-01
          相关资源
          最近更新 更多