【问题标题】:Pythonic way to write a loopPythonic 编写循环的方法
【发布时间】:2014-11-04 13:25:41
【问题描述】:

我有两个列表:a = [1, 2, 3]b = [4, 5, 6]

我在python中使用了两个循环从a的每个元素中减去b的每个元素。

import numpy as np
a = [1, 2, 3]
b = [4, 5, 6]
p = -1
result = np.zeros(len(a)*len(a))
for i in range(0,len(a)):
    for j in range(0,len(a)):
        p = p + 1
        result[p] = a[i] - b[j]

我的结果是正确的:result = [-3., -4., -5., -2., -3., -4., -1., -2., -3.]

但是,我想知道是否有更优雅('pythonic')的方式来做到这一点。

【问题讨论】:

  • 你没有提供完整的代码
  • 与其让它更“pythonic”,这是一个荒谬的想法,不如让这段代码更容易阅读。首先让变量名更有意义。
  • @Puciek 无法理解您的意思,因为我认为这很清楚。如果不是,请纠正我。
  • @Puciek 因为您肯定比我更专业,请告诉我如何清楚 i,j 变量。非常感谢一个非常初学者的程序员。谢谢。
  • @DimKoim 好吧,问题是 - 他们应该代表什么?名称应该与此相关。因此,如果一个变量包含字母“J”,那么名称“j”就可以了,但如果它包含列表的一部分,那么它应该传达相关元素的名称,在这种情况下,“数字”将是不错的名字。但是命名“i”或“j”的麻烦来自于列表被命名为“a”和“b”的事实,因此我们不知道它们代表什么。阅读中间部分,了解变量重构的示例,以及它的重要性:tinyurl.com/oasu2q2

标签: python list loops


【解决方案1】:

没有必要使用索引。您可以迭代这些值。

a = [1, 2, 3]
b = [4, 5, 6]
result = []
for x in a:
    for y in b:
        result.append(x - y)

pythonic 方式是列表推导式。

a = [1, 2, 3]
b = [4, 5, 6]
result = [x - y for x in a for y in b]

请记住,您应该在实际代码中为abxy 使用有意义的名称。

【讨论】:

  • 我会使用列表理解来实现它。我认为它是最 Pythonic 的,因为它在适当的地方使用了函数式编程。
【解决方案2】:

既然你已经在使用numpy,就这么简单:

In [29]: numpy.repeat(a, len(b)) - numpy.tile(b, len(a))
Out[29]: array([-3, -4, -5, -2, -3, -4, -1, -2, -3])

对于 cmets 中的每个请求,OP 想要 1d 输出大小 N^2(虽然 2d 可能更自然),因为 numpy 提供了方便的函数来将 N 大小的数组扩展到 N^M,即 repeattile

numpy.repeat([1,0], 2)
array([1, 1, 0, 0])

numpy.tile([1,0], 2)
array([1, 0, 1, 0])

一旦ab 都被重新格式化为p 的形状,这就是numpy 中原生的逐元素减法问题。

【讨论】:

  • 这很聪明,也很高效,但你应该解释它是如何工作的。
【解决方案3】:

Pythonic 方式将使用itertools.product,因为它返回传递给它的迭代的笛卡尔积。由于不涉及 Python 循环,因此与使用循环的版本相比,它会更快:

>>> from operator import sub
>>> from itertools import starmap, product
>>> list(starmap(sub, product(a, b)))
[-3, -4, -5, -2, -3, -4, -1, -2, -3]

在 NumPy 中,您可以使用提到的食谱 here

>>> arr = np.dstack(np.meshgrid(a, b)).reshape(-1, 2)
>>> np.subtract(arr[:,0], arr[:,1])
array([-3, -2, -1, -4, -3, -2, -5, -4, -3])

时间比较:

>>> b = [4, 5, 6]*1000
>>> a = [1, 2, 3]*1000
>>> %timeit list(starmap(sub, product(a, b)))
1 loops, best of 3: 464 ms per loop
>>> %timeit [x - y for x in a for y in b]
1 loops, best of 3: 491 ms per loop
>>> %%timeit                                 
result = []
for x in a:
    for y in b:
        result.append(x - y) #attribute lookup is slow
... 
1 loops, best of 3: 908 ms per loop
>>> %%timeit
result = [];append = result.append 
for x in a:
    for y in b:
        append(x - y)
... 
1 loops, best of 3: 617 ms per loop
#Numpy version will be little faster if a and b were nd arrays.
>>> %timeit arr = np.dstack(np.meshgrid(a, b)).reshape(-1, 2);np.subtract(arr[:,0], arr[:,1])
1 loops, best of 3: 573 ms per loop

【讨论】:

  • 我的repeat/tile hack 和你的数据OP*1000 得到 147 毫秒
  • @qarma 我同意它是最快的。 +1(我的系统有 265 毫秒)
猜你喜欢
  • 1970-01-01
  • 2010-12-26
  • 2019-07-19
  • 2018-06-06
  • 2018-04-28
  • 2016-10-10
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
相关资源
最近更新 更多