在Python中按元素连接两个字符串列表,而不嵌套for循环

在Python中按元素连接两个字符串列表,而不嵌套for循环,python,string,concatenation,nested-loops,Python,String,Concatenation,Nested Loops,我有两个字符串列表: ls1=['a'、'b'、'c'、'd']和ls2=['k'、'j'、'l'、'm'] 我想创建第三个列表:ls3=['a-k','a-j','a-l','a-m','b-k','b-j','b-l','b-m','d-m',它有16个元素 通过以下嵌套for循环,我可以很容易地实现这一点 然而,这并不是很像Python,它揭示了我的C代码背景 我尝试使用map和lambda实现更具Python风格的解决方案: 但我还不知道我在做什么 用什么样的Pythonic方法来实现我

我有两个字符串列表: ls1=['a'、'b'、'c'、'd']和ls2=['k'、'j'、'l'、'm']

我想创建第三个列表:ls3=['a-k','a-j','a-l','a-m','b-k','b-j','b-l','b-m','d-m',它有16个元素

通过以下嵌套for循环,我可以很容易地实现这一点

然而,这并不是很像Python,它揭示了我的C代码背景

我尝试使用map和lambda实现更具Python风格的解决方案:

但我还不知道我在做什么

用什么样的Pythonic方法来实现我对嵌套for循环所做的工作?

您可以将itertools.product与map一起使用:

正确性和时间测试:

对于基准的常见免责声明适用

在测试条件下,上述产品图解决方案比naive list comprehension naive更快,尽管对于小问题规模来说,差距很小

大部分加速似乎是由于避免了列表理解。事实上,如果map被列表理解产品compr所取代,那么该产品的伸缩性仍然比单纯的方法要好,但在小问题规模方面落后:

small (4x4)
results equal: True True
naive             0.002420 ms
product compr     0.003211 ms
product map       0.002146 ms
large (4x4x4x4x4x4)
results equal: True True
naive             0.836124 ms
product compr     0.681193 ms
product map       0.385240 ms
供参考的基准脚本

import itertools
import timeit

lists = [[chr(97 + 4*i + j) for j in range(4)] for i in range(6)]

print('small (4x4)')
print('results equal:', [x+'-'+y for x in lists[0] for y in lists[1]]
      ==
      list(map('-'.join, itertools.product(lists[0], lists[1]))), end=' ')
print(['-'.join(t) for t in  itertools.product(lists[0], lists[1])]
      ==
      list(map('-'.join, itertools.product(lists[0], lists[1]))))

print('{:16s} {:9.6f} ms'.format('naive', timeit.timeit(lambda: [x+'-'+y for x in lists[0] for y in lists[1]], number=1000)))
print('{:16s} {:9.6f} ms'.format('product compr', timeit.timeit(lambda: ['-'.join(t) for t in itertools.product(lists[0], lists[1])], number=1000)))
print('{:16s} {:9.6f} ms'.format('product map', timeit.timeit(lambda: list(map('-'.join, itertools.product(lists[0], lists[1]))), number=1000)))

print('large (4x4x4x4x4x4)')
print('results equal:', ['-'.join((u, v, w, x, y, z)) for u in lists[0] for v in lists[1] for w in lists[2] for x in lists[3] for y in lists[4] for z in lists[5]]
      ==
      list(map('-'.join, itertools.product(*lists))), end=' ')
print(['-'.join(t) for t in  itertools.product(*lists)]
      ==
      list(map('-'.join, itertools.product(*lists))))

print('{:16s} {:9.6f} ms'.format('naive', timeit.timeit(lambda: ['-'.join((u, v, w, x, y, z)) for u in lists[0] for v in lists[1] for w in lists[2] for x in lists[3] for y in lists[4] for z in lists[5]], number=1000)))
print('{:16s} {:9.6f} ms'.format('product compr', timeit.timeit(lambda: ['-'.join(t) for t in  itertools.product(*lists)], number=1000)))
print('{:16s} {:9.6f} ms'.format('product map', timeit.timeit(lambda: list(map('-'.join, itertools.product(*lists))), number=1000)))
您可以将itertools.product与map一起使用:

正确性和时间测试:

对于基准的常见免责声明适用

在测试条件下,上述产品图解决方案比naive list comprehension naive更快,尽管对于小问题规模来说,差距很小

大部分加速似乎是由于避免了列表理解。事实上,如果map被列表理解产品compr所取代,那么该产品的伸缩性仍然比单纯的方法要好,但在小问题规模方面落后:

small (4x4)
results equal: True True
naive             0.002420 ms
product compr     0.003211 ms
product map       0.002146 ms
large (4x4x4x4x4x4)
results equal: True True
naive             0.836124 ms
product compr     0.681193 ms
product map       0.385240 ms
供参考的基准脚本

import itertools
import timeit

lists = [[chr(97 + 4*i + j) for j in range(4)] for i in range(6)]

print('small (4x4)')
print('results equal:', [x+'-'+y for x in lists[0] for y in lists[1]]
      ==
      list(map('-'.join, itertools.product(lists[0], lists[1]))), end=' ')
print(['-'.join(t) for t in  itertools.product(lists[0], lists[1])]
      ==
      list(map('-'.join, itertools.product(lists[0], lists[1]))))

print('{:16s} {:9.6f} ms'.format('naive', timeit.timeit(lambda: [x+'-'+y for x in lists[0] for y in lists[1]], number=1000)))
print('{:16s} {:9.6f} ms'.format('product compr', timeit.timeit(lambda: ['-'.join(t) for t in itertools.product(lists[0], lists[1])], number=1000)))
print('{:16s} {:9.6f} ms'.format('product map', timeit.timeit(lambda: list(map('-'.join, itertools.product(lists[0], lists[1]))), number=1000)))

print('large (4x4x4x4x4x4)')
print('results equal:', ['-'.join((u, v, w, x, y, z)) for u in lists[0] for v in lists[1] for w in lists[2] for x in lists[3] for y in lists[4] for z in lists[5]]
      ==
      list(map('-'.join, itertools.product(*lists))), end=' ')
print(['-'.join(t) for t in  itertools.product(*lists)]
      ==
      list(map('-'.join, itertools.product(*lists))))

print('{:16s} {:9.6f} ms'.format('naive', timeit.timeit(lambda: ['-'.join((u, v, w, x, y, z)) for u in lists[0] for v in lists[1] for w in lists[2] for x in lists[3] for y in lists[4] for z in lists[5]], number=1000)))
print('{:16s} {:9.6f} ms'.format('product compr', timeit.timeit(lambda: ['-'.join(t) for t in  itertools.product(*lists)], number=1000)))
print('{:16s} {:9.6f} ms'.format('product map', timeit.timeit(lambda: list(map('-'.join, itertools.product(*lists))), number=1000)))

您所使用的技术完全是python式的,在将列表理解引入语言之前,它是规范的。但是,您建议使用zip的方法是行不通的,因为您需要来自ls1和ls2的所有元素对,但zip只是使用相应的元素而不是所有组合创建元素对

如果您想使用更紧凑的代码,那么适当的列表理解应该是

ls3 = [x+'-'+y for x in ls1 for y in ls2]

对于大型列表,或者您需要每一盎司的性能,而这绝不是您首先考虑的问题,请参见@PaulPanzer的答案,他解释了一种更有效但稍微复杂的技术。

您使用的技术是完美的Pythonic,在列表理解被引入语言之前,它是规范的。但是,您建议使用zip的方法是行不通的,因为您需要来自ls1和ls2的所有元素对,但zip只是使用相应的元素而不是所有组合创建元素对

如果您想使用更紧凑的代码,那么适当的列表理解应该是

ls3 = [x+'-'+y for x in ls1 for y in ls2]

对于大型列表,或者您需要的每一盎司性能都不应该是您的首要考虑,请参阅@PaulPanzer的答案,谁解释了一种更有效但稍微复杂的技术。

您的嵌套for循环是Pythonic的。您的嵌套for循环是Pythonic的。这非常不必要地使用库模块来提供一种只使用内置语言构造就能更快地工作的解决方案,尽管它确实有回答OP的问题的优点为了避免嵌套循环,它付出了巨大的代价。我认为@holdenweb是不必要的关键。此解决方案比列表理解方法更快,明确表明它生成的是输入的乘积,并且可以轻松地推广到任意数量的输入。我认为这两个答案都教给我们一些有用的东西。啊,温和的声音。谢谢@mfripp,欢迎您的评论。批评并不一定意味着不赞成@PaulPanzer已经清楚地表明,我错误地断言使用库模块是完全不必要的。他付出的额外工作给了他更多的荣誉。我已经撤回了我的反对票。@holdenweb批评并不一定意味着不赞成。当它被否决票所强化时,它通常会这样做。无论如何,感谢您回到这里,我们对此表示感谢。这完全不必要地使用库模块来提供一个只使用内置语言结构就能更快工作的解决方案,尽管它确实有回答OP关于如何避免嵌套循环的问题的优点,我认为@holdenweb的批评是不必要的。此解决方案比列表理解方法更快,明确表明它生成的是输入的乘积,并且可以轻松地推广到任意数量的输入。我认为这两个答案都教给我们一些有用的东西。啊,温和的声音。谢谢@mfripp,欢迎您的评论。批评并不一定意味着不赞成@保尔·帕尼泽已经清楚地表明我错了
断言使用库模块是非常不必要的。他付出的额外工作给了他更多的荣誉。我已经撤回了我的反对票。@holdenweb批评并不一定意味着不赞成。当它被否决票所强化时,它通常会这样做。无论如何,谢谢你回来,非常感谢。