【问题标题】:Pairs from single list来自单个列表的对
【发布时间】:2011-06-05 10:11:27
【问题描述】:

我经常发现需要成对处理列表。我想知道哪一种是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 模块。

标签: python list zip slice idioms


【解决方案1】:

我最喜欢的做法:

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)
  • 对于您的第二个解决方案,如果追求性能,您不想避免创建列表吗?
【解决方案2】:

我会说您的初始解决方案 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)]

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      就大多数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

      【讨论】:

        【解决方案5】:

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

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

        确保所有元素都包含在内的正确方法是什么?

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

        【讨论】:

          【解决方案6】:
          >>> 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)]
          

          【讨论】:

          • 索引错误:从空列表中弹出
          • @HQuser 当然,如果列表中有奇数个项目,您会收到该错误。您必须确定您有配对或检查此错误情况。
          【解决方案7】:

          只做:

          >>> 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:])),它不会将列表拆分成对。
          【解决方案8】:

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

          >>> 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]
          

          【讨论】:

            【解决方案9】:

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

            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
            • 如果列表长度不均匀,则删除最新的元素
            猜你喜欢
            • 2016-07-10
            • 1970-01-01
            • 2021-02-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-10-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多