Verständnis für die Abflachung einer Sequenz von Sequenzen?

Wenn ich Sequenzen von Sequenzen habe (vielleicht eine Liste von Tupeln), kann ich itertools.chain () verwenden, um sie zu reduzieren. Aber manchmal fühle ich mich, als würde ich es lieber als ein Verständnis schreiben. Ich kann einfach nicht herausfinden, wie es geht. Hier ist ein sehr konstruierter Fall:

Nehmen wir an, ich möchte die Elemente jedes Paares in einer Sequenz austauschen. Ich verwende hier eine Zeichenfolge als Sequenz:

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

Ich benutze Zip in den geraden und ungeraden Schichten der Sequenz, um die Paare zu tauschen. Aber ich lande mit einer Liste von Tupeln, die jetzt abgeflacht werden müssen. Also benutze ich chain (). Könnte ich es stattdessen mit einem Verständnis express?

Wenn Sie Ihre eigene Lösung für das grundlegende Problem des Austausches von Elementen der Paare veröffentlichen wollen, gehen Sie voran, ich stimme alles ab, was mir etwas Neues beibringt. Aber ich akzeptiere nur eine Antwort, die auf meine Frage ausgerichtet ist, auch wenn die Antwort “Nein, du kannst nicht” lautet.

    Mit einem Verständnis? Gut…

     >>> seq = '012345' >>> swapped_pairs = zip(seq[1::2], seq[::2]) >>> ''.join(item for pair in swapped_pairs for item in pair) '103254' 

    Am schnellsten habe ich gefunden, mit einem leeren Array zu beginnen und es zu erweitern:

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

    Das ist doppelt so schnell für das Beispiel von Alex Martellis Versuch: Eine flache Liste aus einer Liste von Listen in Python zu machen

     $ 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 

    Ich kam auf diese Idee, weil ich eine Ahnung hatte, dass extend hinter den Kulissen die richtige Menge an Speicher für die Liste zuweist und wahrscheinlich etwas Low-Level-Code verwendet, um Objekte zu verschieben. Ich habe keine Ahnung, ob das stimmt, aber Wen kümmert es, es ist schneller.

    Übrigens, es ist nur eine lineare Beschleunigung:

     $ 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 

    Sie können auch die map(results.extend, a) , aber diese ist langsamer, da sie ihre eigene Liste von Nonen erstellt.

    Es bietet Ihnen auch einige Vorteile, wenn Sie keine funktionale Programmierung verwenden. dh

    • Sie können eine bestehende Liste erweitern, anstatt eine leere Liste zu erstellen.
    • Sie können den Code auf einen Blick, Minuten, Tage oder sogar Monate später noch verstehen.

    Übrigens wahrscheinlich am besten Listenkompressen vermeiden. Kleine sind nicht so schlecht, aber im Allgemeinen ersparen Listenkompressen Ihnen nicht viel Schreibarbeit, sind aber oft schwerer zu verstehen und sehr schwer zu ändern oder zu refaktorieren (jemals ein dreistufiges Listenverständnis gesehen?). Die Codierungsrichtlinien von Google raten davon ab, in Ausnahmefällen. Meiner Meinung nach sind sie nur nützlich für “Wegwerf” -Code, dh Code, bei dem der Autor sich nicht um die Lesbarkeit kümmert, oder Code, von dem man weiß, dass er nie zukünftige Wartung benötigt.

    Vergleichen Sie diese zwei Arten, dasselbe zu schreiben:

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

    mit diesem:

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

    YMMV, aber der erste hat mich aufgehalten und ich musste darüber nachdenken. In der zweiten wird die Verschachtelung aus der Einrückung ersichtlich.

    Sie könnten reduzieren, um Ihr Ziel zu erreichen:

     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) 

    Dies gibt ein Tupel anstelle einer Liste zurück, da die Elemente in Ihrer ursprünglichen Liste Tupel sind, die verkettet werden. Aber Sie können daraus leicht eine Liste erstellen und die Join-Methode akzeptiert auch Tupel.

    Ein Listenverständnis ist dafür übrigens nicht das richtige Werkzeug. Im Grunde erstellt ein Listenverständnis eine neue Liste, indem es beschreibt, wie die Elemente dieser Liste aussehen sollen. Sie möchten eine Liste von Elementen auf nur einen Wert reduzieren.

     >>> a = [(1, 2), (3, 4), (5, 6)] >>> reduce(tuple.__add__, a) >>> (1, 2, 3, 4, 5, 6) 

    Oder, um agnostisch zu sein bezüglich der Art der inneren Sequenzen (solange sie alle gleich sind):

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