397 Stimmen

Wie kann man eine Liste anhand einer Bedingung aufteilen?

Was ist der beste Weg, sowohl ästhetisch als auch von der Leistung her, um eine Liste von Elementen in mehrere Listen auf der Grundlage einer Bedingung aufzuteilen? Das Äquivalent von:

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

Gibt es eine elegantere Möglichkeit, dies zu tun?

Hier ist der eigentliche Anwendungsfall, um besser zu erklären, was ich zu tun versuche:

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

9voto

Biga Punkte 494
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]

append gibt keine zurück, also funktioniert es.

6voto

BJ Homer Punkte 48238

Mir persönlich gefällt die von Ihnen zitierte Version, vorausgesetzt, Sie haben bereits eine Liste von goodvals herumhängen. Wenn nicht, etwas wie:

good = filter(lambda x: is_good(x), mylist)
bad = filter(lambda x: not is_good(x), mylist)

Das ist natürlich sehr ähnlich wie die Verwendung einer Listenverarbeitung, wie Sie es ursprünglich getan haben, aber mit einer Funktion anstelle eines Nachschlagewerks:

good = [x for x in mylist if is_good(x)]
bad  = [x for x in mylist if not is_good(x)]

Im Allgemeinen finde ich die Ästhetik von Listenverzeichnissen sehr ansprechend. Natürlich, wenn Sie nicht wirklich brauchen, um die Reihenfolge zu bewahren und brauchen keine Duplikate, verwenden Sie die intersection y difference Methoden am Set würden ebenfalls gut funktionieren.

5voto

michau Punkte 1443

Wenn Sie es im FP-Stil machen wollen:

good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
                                        for y in mylist)) ]

Nicht die lesbarste Lösung, aber zumindest wird mylist nur einmal durchlaufen.

5voto

FunkySayu Punkte 6786

Manchmal sieht es so aus, als ob das Verstehen von Listen nicht die beste Lösung ist!

Ich habe einen kleinen Test gemacht, basierend auf den Antworten, die die Leute zu diesem Thema gegeben haben, getestet an einer zufällig generierten Liste. Hier ist die Generierung der Liste (es gibt wahrscheinlich einen besseren Weg zu tun, aber das ist nicht der Punkt):

good_list = ('.jpg','.jpeg','.gif','.bmp','.png')

import random
import string
my_origin_list = []
for i in xrange(10000):
    fname = ''.join(random.choice(string.lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(good_list)
    else:
        fext = "." + ''.join(random.choice(string.lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

Und los geht's

# Parand
def f1():
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def f2():
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def f3():
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# Ants Aasma
def f4():
    l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
    return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def f5():
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def f6():
    return filter(lambda e: e[2] in good_list, my_origin_list), filter(lambda e: not e[2] in good_list, my_origin_list)

Die Verwendung des cmpthese Funktion ist das beste Ergebnis die dbr-Antwort:

f1     204/s  --    -5%   -14%   -15%   -20%   -26%
f6     215/s     6%  --    -9%   -11%   -16%   -22%
f3     237/s    16%    10%  --    -2%    -7%   -14%
f4     240/s    18%    12%     2%  --    -6%   -13%
f5     255/s    25%    18%     8%     6%  --    -8%
f2     277/s    36%    29%    17%    15%     9%  --

4voto

Hanfei Sun Punkte 41905
def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

Siehe este

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X