ChatGPT解决这个技术问题 Extra ChatGPT

来自单个列表的对[重复]

这个问题在这里已经有了答案:如何将列表分成大小相等的块? (70 个回答) 22 天前关闭。自 14 天前起,社区正在审查是否重新提出此问题。

通常,我发现需要成对处理列表。我想知道哪一种是pythonic和有效的方法,并在谷歌上找到了这个:

pairs = zip(t[::2], t[1::2])

我认为这已经足够pythonic了,但是在最近涉及 idioms versus efficiency 的讨论之后,我决定做一些测试:

import time
from itertools import islice, izip

def pairs_1(t):
    return zip(t[::2], t[1::2]) 

def pairs_2(t):
    return izip(t[::2], t[1::2]) 

def pairs_3(t):
    return izip(islice(t,None,None,2), islice(t,1,None,2))

A = range(10000)
B = xrange(len(A))

def pairs_4(t):
    # ignore value of t!
    t = B
    return izip(islice(t,None,None,2), islice(t,1,None,2))

for f in pairs_1, pairs_2, pairs_3, pairs_4:
    # time the pairing
    s = time.time()
    for i in range(1000):
        p = f(A)
    t1 = time.time() - s

    # time using the pairs
    s = time.time()
    for i in range(1000):
        p = f(A)
        for a, b in p:
            pass
    t2 = time.time() - s
    print t1, t2, t2-t1

这些是我电脑上的结果:

1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578

如果我对它们的解释正确,那应该意味着 Python 中列表、列表索引和列表切片的实现非常有效。这是一个既令人欣慰又出乎意料的结果。

还有另一种“更好”的成对遍历列表的方式吗?

请注意,如果列表具有奇数个元素,则最后一个将不在任何对中。

哪种方法是确保包含所有元素的正确方法?

我从测试的答案中添加了这两个建议:

def pairwise(t):
    it = iter(t)
    return izip(it, it)

def chunkwise(t, size=2):
    it = iter(t)
    return izip(*[it]*size)

这些是结果:

0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176

到目前为止的结果

大多数pythonic和非常有效的:

pairs = izip(t[::2], t[1::2])

最高效且非常 Pythonic:

pairs = izip(*[iter(t)]*2)

我花了一点时间才发现第一个答案使用了两个迭代器,而第二个答案使用了一个。

为了处理具有奇数个元素的序列,建议增加原始序列,添加一个与前一个最后一个元素配对的元素 (None),这可以通过 itertools.izip_longest() 实现。

最后

请注意,在 Python 3.x 中,zip() 表现为 itertools.izip(),而 itertools.izip() 消失了。

RE:“正确的方式”——没有“正确”的方式!这取决于用例。
@Andrew Jaffe 在这种情况下,我给出了“最佳”的标准:高效且 Pythonic。
@Apalala:我的意思是奇数的结果取决于使用情况。例如:您可以省略最后一个元素,或添加特定的已知虚拟元素,或复制最后一个
@Apalala:因为您使用的是一些 mumbo-jumbo 而不是 timeit 模块。

t
ti7

我最喜欢的做法:

def pairwise(t):
    it = iter(t)
    return zip(it,it)

# for "pairs" of any length
def chunkwise(t, size=2):
    it = iter(t)
    return zip(*[it]*size)

当您想配对所有元素时,您显然可能需要一个填充值:

from itertools import izip_longest
def blockwise(t, size=2, fillvalue=None):
    it = iter(t)
    return izip_longest(*[it]*size, fillvalue=fillvalue)

在 Python 3 中,itertools.izip 现在只是 zip .. 要使用较旧的 Python,请使用

from itertools import izip as zip

第一个(成对)函数似乎缺少第二个迭代器的克隆和推进。请参阅 itertools 食谱部分。
@Apalala: zip 确实将相同的迭代器推进了两次。
当然,你是对的,pairwise 是目前为止效率最高的,不知道为什么。
我喜欢这个解决方案:它很懒,而且它利用了迭代器的状态来产生很好的效果。您甚至可以将其设为单行,尽管可能会牺牲可读性:izip(*[iter(t)]*size)
对于您的第二个解决方案,如果追求性能,您不想避免创建列表吗?
T
Tim Pietzcker

我会说您的初始解决方案 pairs = zip(t[::2], t[1::2]) 是最好的解决方案,因为它最容易阅读(在 Python 3 中,zip 自动返回迭代器而不是列表)。

为确保包含所有元素,您只需将列表扩展 None

然后,如果列表有奇数个元素,最后一对将是 (item, None)

>>> t = [1,2,3,4,5]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, None)]
>>> t = [1,2,3,4,5,6]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, 6)]

T
Tomasz Elendt

我从小免责声明开始 - 不要使用下面的代码。它根本不是 Pythonic,我只是为了好玩而写的。它类似于@THC4k pairwise 函数,但它使用 iterlambda 闭包。它不使用 itertools 模块,也不支持 fillvalue。我把它放在这里是因为有人可能会觉得它很有趣:

pairwise = lambda t: iter((lambda f: lambda: (f(), f()))(iter(t).next), None)

A
Andras Deak -- Слава Україні

就大多数 pythonic 而言,我会说 recipes supplied in the python source docs (其中一些看起来很像@JochenRitzel 提供的答案)可能是你最好的选择;)

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

在现代 python 上,您只需要使用 zip_longest(*args, fillvalue=fillvalue) according to the corresponding doc page


A
Aaron Digulla

还有另一种“更好”的成对遍历列表的方式吗?

我不能肯定地说,但我对此表示怀疑:任何其他遍历都将包含更多必须解释的 Python 代码。像 zip() 这样的内置函数是用 C 编写的,速度要快得多。

哪种方法是确保包含所有元素的正确方法?

检查列表的长度,如果它是奇数 (len(list) & 1 == 1),则复制列表并附加一个项目。


D
Diarmuid O'Briain
>>> my_list = [1,2,3,4,5,6,7,8,9,10]
>>> my_pairs = list()
>>> while(my_list):
...     a = my_list.pop(0); b = my_list.pop(0)
...     my_pairs.append((a,b))
... 
>>> print(my_pairs)
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

IndexError:从空列表中弹出
@HQuser 当然,如果列表中有奇数个项目,您会收到该错误。您必须确定您有配对或检查此错误情况。
I
Israel Gonçaves de Oliveira

只做:

>>> l = [1, 2, 3, 4, 5, 6]
>>> [(x,y) for x,y in zip(l[:-1], l[1:])]
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

您的代码等效于更简单的 list(zip(l, l[1:])),它不会将列表拆分成对。
最佳答案来自@Apalala
f
frainmaster

以防万一有人需要算法方面的答案,这里是:

>>> def getPairs(list):
...     out = []
...     for i in range(len(list)-1):
...         a = list.pop(0)
...         for j in a:
...             out.append([a, j])
...     return b
>>> 
>>> k = [1, 2, 3, 4]
>>> l = getPairs(k)
>>> l
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

但请注意,您的原始列表也将减少到最后一个元素,因为您在其上使用了 pop

>>> k
[4]

V
Vlad Bezden

这是使用生成器创建对/腿的示例。生成器不受堆栈限制

def pairwise(data):
    zip(data[::2], data[1::2])

例子:

print(list(pairwise(range(10))))

输出:

[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]

执行时间比较?
该列表没有分成对,因为原始列表中的大多数数字都出现在两个元组中。预期输出为 [(0, 1), (2, 3), (4, 5)....
@Apalala 感谢您指出。我修复了代码以提供正确的输出
zip() 已经在 Python 3.x 中返回了一个生成器,@VladBezden
如果列表长度不是偶数,最新的元素将被丢弃
S
SherylHohman

这个片段对我有用。如果列表长度为奇数 (fillvalue=""),它会创建元组对并将空字符串添加到最后一对。

zip_longest(*[iter(my_list)] * 2, fillvalue="")

# odd
list(zip_longest(*[iter([0, 1, 2, 3, 4, 5, 6])] * 2, fillvalue=""))
[(0, 1), (2, 3), (4, 5), (6, '')]

# even
list(zip_longest(*[iter([0, 1, 2, 3, 4, 5])] * 2, fillvalue=""))
[(0, 1), (2, 3), (4, 5)]