Compréhension pour aplatir une séquence de séquences? [dupliquer]

cette question a déjà une réponse ici:

si j'ai une séquence de séquences (peut-être une liste de tuples) je peux utiliser des itertools.chaîne() pour l'aplatir. Mais parfois j'ai l'impression que je préférerais l'écrire compréhension. Je ne peux pas comprendre comment le faire. Voici un cas très bien interprété:

disons que je veux echanger les éléments de chaque paire dans une séquence. J'utilise une chaîne de caractères comme séquence ici:

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

j'utilise zip sur les tranches paires et impaires de la séquence pour échanger les paires. Mais je finis avec une liste de tuples qui doivent maintenant être aplatis. Donc j'utilise chain (). Y a-t-il un moyen que je puisse l'exprimer avec une compréhension à la place?

si vous voulez poster votre propre solution au problème de base de l'échange d'éléments des paires, allez-y, je vais voter tout ce qui m'apprend quelque chose de nouveau. Mais je ne marque accepté une réponse ciblée sur ma question, même si la réponse est "Non, vous ne pouvez pas.".

35
demandé sur PEZ 2009-01-19 13:47:39

4 réponses

avec une compréhension? Bien...

>>> seq = '012345'
>>> swapped_pairs = zip(seq[1::2], seq[::2])
>>> ''.join(item for pair in swapped_pairs for item in pair)
'103254'
29
répondu nosklo 2009-01-19 11:04:17

le plus rapide que j'ai trouvé est de commencer avec un tableau vide et de l'étendre:

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

C'est plus de deux fois plus rapide pour l'exemple dans la tentative D'Alex Martelli sur: faire une liste plate hors de la liste des listes en 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

je suis venu avec ceci parce que j'avais une intuition que dans les coulisses, étendre attribuerait la bonne quantité de mémoire pour la liste, et utilise probablement un code de bas niveau pour déplacer des articles dans. Je n'ai aucune idée si c'est vrai, mais qui s'en soucie, c'est plus rapide.

soit dit en passant, ce n'est qu'une accélération linéaire:

$ 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

vous pouvez également utiliser le map(results.extend, a) , mais c'est plus lent car il construit sa propre liste de Nones.

il vous donne également certains des avantages de ne pas utiliser la programmation fonctionnelle. c'est à dire

  • vous pouvez étendre une liste existante au lieu d'en créer une vide,
  • vous pouvez encore comprendre le code d'un coup d'oeil, des minutes, des jours ou même des mois plus tard.

d'ailleurs, il est probablement préférable d'éviter les interprétations de listes. Les petits ne sont pas trop mauvais, mais en général les compréhensions de liste ne vous épargnent pas vraiment beaucoup de Dactylographie, mais sont souvent plus difficiles à comprendre et très difficile à changer ou à remanier (vous avez déjà vu une compréhension de liste à trois niveaux?). les lignes directrices de Google en matière de codage les déconseillent, sauf dans les cas suivants: les cas les plus simples. mon opinion est qu'ils ne sont utiles que dans le code 'jeter', c.-à-d. le code où l'auteur ne se soucie pas de la lisibilité, ou le code qui est connu pour ne jamais nécessiter une maintenance future.

Comparer ces deux façons d'écrire la même chose:

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

avec ceci:

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

YMMV, mais le premier m'a arrêté dans mes traces et j'ai dû y réfléchir. Dans la seconde, la nidification est évident à partir de l'indentation.

16
répondu Mike A 2017-05-23 11:33:24

vous pouvez utiliser reduce pour atteindre votre objectif:

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)

Ce retour, un n-uplet au lieu d'une liste parce que les éléments de votre liste d'origine sont des n-uplets qui se concaténées. Mais vous pouvez facilement construire une liste à partir de cela et la méthode join accepte aussi les tuples.

la compréhension d'une liste n'est pas, soit dit en passant, le bon outil pour cela. Fondamentalement, une compréhension de liste construit une nouvelle liste en décrivant la façon dont les éléments de cette liste devrait ressembler comme. Vous souhaitez réduire une liste d'éléments à une seule valeur.

3
répondu 2009-01-19 10:59:23
>>> a = [(1, 2), (3, 4), (5, 6)]
>>> reduce(tuple.__add__, a)
>>> (1, 2, 3, 4, 5, 6)

Ou, pour être agnostique sur le type de l'intérieur des séquences (tant qu'ils sont tout de même):

>>> reduce(a[0].__class__.__add__, a)
1
répondu Arkady 2009-01-19 14:34:44