代码之家  ›  专栏  ›  技术社区  ›  PEZ

对一系列序列的扁平化理解?[副本]

  •  44
  • PEZ  · 技术社区  · 17 年前

    如果我有序列(可能是元组列表),我可以使用itertools.chain()来展开它。但有时我觉得我宁愿把它写成一个理解。我只是不知道该怎么做。这是一个非常有解释力的案例:

    假设我想交换序列中每对元素的元素。我在这里使用字符串作为序列:

    >>> from itertools import chain
    >>> seq = '012345'
    >>> swapped_pairs = zip(seq[1::2], seq[::2])
    >>> swapped_pairs
    [('1', '0'), ('3', '2'), ('5', '4')]
    >>> "".join(chain(*swapped_pairs))
    '103254'
    

    我在序列的偶数和奇数切片上使用zip来交换配对。但我最终得到了一个现在需要扁平化的元组列表。所以我用chain()。有没有一种方法可以让我用理解来表达它?

    如果你想发布你自己的解决方案来解决交换成对元素的基本问题,那就去吧,我会对任何能教会我新东西的东西投赞成票。但我只会将针对我的问题的答案标记为已接受,即使答案是“不,你不能”。

    4 回复  |  直到 17 年前
        1
  •  32
  •   nosklo    17 年前

    理解?好。..

    >>> seq = '012345'
    >>> swapped_pairs = zip(seq[1::2], seq[::2])
    >>> ''.join(item for pair in swapped_pairs for item in pair)
    '103254'
    
        2
  •  16
  •   Community Mohan Dere    9 年前

    我发现最快的方法是从一个空数组开始并扩展它:

    In [1]: a = [['abc', 'def'], ['ghi'],['xzy']]
    
    In [2]: result = []
    
    In [3]: extend = result.extend
    
    In [4]: for l in a:
       ...:     extend(l)
       ...: 
    
    In [5]: result
    Out[5]: ['abc', 'def', 'ghi', 'xzy']
    

    这是Alex Martelli尝试的两倍多: Making a flat list out of list of lists in Python

    $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
    10000 loops, best of 3: 86.3 usec per loop
    
    $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99'  'b = []' 'extend = b.extend' 'for sub in l:' '    extend(sub)'
    10000 loops, best of 3: 36.6 usec per loop
    

    我提出这个想法是因为我有一种预感,在幕后,extend会为列表分配适量的内存,并可能使用一些低级代码来移动项目。我不知道这是不是真的,但谁在乎呢,它更快。

    顺便说一句,这只是线性加速:

    $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]'  'b = []' 'extend = b.extend' 'for sub in l:' '    extend(sub)'
    1000000 loops, best of 3: 0.844 usec per loop
    
    $ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]' '[item for sublist in l for item in sublist]'
    1000000 loops, best of 3: 1.56 usec per loop
    

    您还可以使用 map(results.extend, a) ,但由于它正在构建自己的Nones列表,因此速度较慢。

    它还为您提供了不使用函数式编程的一些好处。也就是

    • 您可以扩展现有列表,而不是创建空列表,
    • 几分钟、几天甚至几个月后,你仍然可以一目了然地理解代码。

    顺便说一句,最好避免对列表的理解。小的并不太糟糕,但总的来说,列表理解实际上并不能节省你太多的打字时间,但通常更难理解,也很难更改或重构(见过三级列表理解吗?)。 Google coding guidelines advise against them except in simple cases. 我的观点是,它们只在“丢弃”代码中有用,即作者不关心可读性的代码,或者已知永远不需要未来维护的代码。

    比较这两种写同一件事的方法:

    result = [item for sublist in l for item in sublist]
    

    与此:

    result = []
    for sublist in l:
        for item in sublist:
            result.append(item)
    

    YMMV,但第一个让我停下了脚步,我不得不考虑一下。在第二个中,嵌套从缩进中显而易见。

        3
  •  3
  •   unbeknown unbeknown    17 年前

    你可以使用reduce来实现你的目标:

    In [6]: import operator
    In [7]: a = [(1, 2), (2,3), (4,5)]
    In [8]: reduce(operator.add, a, ())
    Out[8]: (1, 2, 2, 3, 4, 5)
    

    这将返回一个元组而不是列表,因为原始列表中的元素是连接起来的元组。但是你可以很容易地从中构建一个列表,并且join方法也接受元组。

    顺便说一句,列表理解不是正确的工具。基本上,列表理解通过描述这个列表的元素应该是什么样子来构建一个新的列表。您希望将元素列表缩减为仅一个值。

        4
  •  1
  •   CrashCodes    17 年前
    >>> a = [(1, 2), (3, 4), (5, 6)]
    >>> reduce(tuple.__add__, a)
    >>> (1, 2, 3, 4, 5, 6)
    

    或者,不知道内部序列的类型(只要它们都是相同的):

    >>> reduce(a[0].__class__.__add__, a)