在没有嵌套 for 循环的情况下 Python 中明智地连接两个字符串列表
Concatenating two lists of Strings element wise in Python without Nested for 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
循环轻松实现此目的
ls3 = []
for elem in ls1:
for item in ls2:
ls3.append(elem+'-'+item)
但是,这不是很 Pythonic 并且揭示了我的 C 代码背景。
我尝试使用 map
和 lambda
的更 Pythonic 解决方案:
[ map(lambda x,y: x+'-'+y, a,b) for a,b in zip(ls1,ls2) ]
但我还不知道我在做什么。
什么是 Pythonic 方法来实现我用嵌套 for
循环所做的事情?
您可以将 itertools.product
与 map
一起使用:
list(map('-'.join, itertools.product('abcd', 'kjlm')))
# ['a-k', 'a-j', 'a-l', 'a-m', 'b-k', 'b-j', 'b-l', 'b-m', 'c-k', 'c-j', 'c-l', 'c-m', 'd-k', 'd-j', 'd-l', 'd-m']
测试正确性和时间:
通常的基准免责声明适用。
在测试条件下,上述 ("product map
") 的解决方案比 "naive" 列表理解 ("naive
") 更快,尽管对于小问题来说差距很小尺码。
大部分加速似乎是由于避免了列表理解。事实上,如果 map
被列表推导式 (product compr
") 取代,那么 product
仍然比原始方法更好地扩展,但在小问题规模上落后:
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)))
您使用的技术完全是 Pythonic 的,并且在列表理解被引入该语言之前一直是规范的。但是,您建议使用 zip
的那个不会起作用,因为您想要来自 ls1
和 ls2
的所有元素对,但是 zip
只是使用相应的元素创建对元素而不是所有组合。
如果您想使用更紧凑的代码,那么适当的列表理解将是
ls3 = [x+'-'+y for x in ls1 for y in ls2]
对于大型列表,或者您需要每一盎司性能的地方(这绝不应该是您的首要考虑因素),请参阅@PaulPanzer 的回答,他解释了一种更有效但稍微复杂一些的技术。
我有两个字符串列表:
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
循环轻松实现此目的
ls3 = []
for elem in ls1:
for item in ls2:
ls3.append(elem+'-'+item)
但是,这不是很 Pythonic 并且揭示了我的 C 代码背景。
我尝试使用 map
和 lambda
的更 Pythonic 解决方案:
[ map(lambda x,y: x+'-'+y, a,b) for a,b in zip(ls1,ls2) ]
但我还不知道我在做什么。
什么是 Pythonic 方法来实现我用嵌套 for
循环所做的事情?
您可以将 itertools.product
与 map
一起使用:
list(map('-'.join, itertools.product('abcd', 'kjlm')))
# ['a-k', 'a-j', 'a-l', 'a-m', 'b-k', 'b-j', 'b-l', 'b-m', 'c-k', 'c-j', 'c-l', 'c-m', 'd-k', 'd-j', 'd-l', 'd-m']
测试正确性和时间:
通常的基准免责声明适用。
在测试条件下,上述 ("product map
") 的解决方案比 "naive" 列表理解 ("naive
") 更快,尽管对于小问题来说差距很小尺码。
大部分加速似乎是由于避免了列表理解。事实上,如果 map
被列表推导式 (product compr
") 取代,那么 product
仍然比原始方法更好地扩展,但在小问题规模上落后:
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)))
您使用的技术完全是 Pythonic 的,并且在列表理解被引入该语言之前一直是规范的。但是,您建议使用 zip
的那个不会起作用,因为您想要来自 ls1
和 ls2
的所有元素对,但是 zip
只是使用相应的元素创建对元素而不是所有组合。
如果您想使用更紧凑的代码,那么适当的列表理解将是
ls3 = [x+'-'+y for x in ls1 for y in ls2]
对于大型列表,或者您需要每一盎司性能的地方(这绝不应该是您的首要考虑因素),请参阅@PaulPanzer 的回答,他解释了一种更有效但稍微复杂一些的技术。