Meine Meinung dazu. Ich schlage eine faule, einseitige Vorgehensweise vor, partition
Funktion, die die relative Reihenfolge in den ausgegebenen Teilsequenzen beibehält.
1. Anforderungen
Ich gehe davon aus, dass dies die Voraussetzungen sind:
- die relative Reihenfolge der Elemente beibehalten (daher keine Mengen und Wörterbücher)
- Bedingung nur einmal für jedes Element auswerten (daher keine Verwendung von (
i
) filter
o groupby
)
- die faule Nutzung einer der beiden Sequenzen ermöglichen (wenn wir es uns leisten können vorzurechnen, dann ist die naive Implementierung wahrscheinlich auch akzeptabel)
2. split
Bibliothek
Meine partition
Funktion (siehe unten) und andere ähnliche Funktionen haben es in eine kleine Bibliothek geschafft:
Es ist normal über PyPI installierbar:
pip install --user split
Um eine Liste aufgrund einer Bedingung aufzuteilen, verwenden Sie partition
Funktion:
>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]
3. partition
Funktion erklärt
Intern müssen wir zwei Teilsequenzen auf einmal erstellen, also verbrauchen wir nur eine Ausgabesequenz verbraucht wird, muss die andere auch berechnet zu berechnen. Außerdem müssen wir den Zustand zwischen den Benutzeranfragen beibehalten (Speicherung von verarbeiteten aber noch nicht angeforderte Elemente). Um den Zustand zu erhalten, verwende ich zwei doppelendige Warteschlangen ( deques
):
from collections import deque
SplitSeq
Klasse kümmert sich um die Haushaltsführung:
class SplitSeq:
def __init__(self, condition, sequence):
self.cond = condition
self.goods = deque([])
self.bads = deque([])
self.seq = iter(sequence)
Magie geschieht in ihrem .getNext()
Methode. Es ist fast wie .next()
der Iteratoren, ermöglicht aber die Angabe der Art des gewünschten Elements dieses Mal. Hinter den Kulissen werden die abgelehnten Elemente nicht verworfen, sondern legt sie stattdessen in einer der beiden Warteschlangen ab:
def getNext(self, getGood=True):
if getGood:
these, those, cond = self.goods, self.bads, self.cond
else:
these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
if these:
return these.popleft()
else:
while 1: # exit on StopIteration
n = self.seq.next()
if cond(n):
return n
else:
those.append(n)
Der Endnutzer soll Folgendes verwenden partition
Funktion. Sie nimmt eine Bedingungsfunktion und eine Sequenz (genau wie map
o filter
), und gibt zwei Generatoren zurück. Der erste Generator bildet eine Teilsequenz von Elementen, für die die Bedingung gilt, der zweite erzeugt die komplementäre Teilsequenz. Iteratoren und Generatoren ermöglichen träge Aufteilung auch langer oder unendlicher Sequenzen.
def partition(condition, sequence):
cond = condition if condition else bool # evaluate as bool if condition == None
ss = SplitSeq(cond, sequence)
def goods():
while 1:
yield ss.getNext(getGood=True)
def bads():
while 1:
yield ss.getNext(getGood=False)
return goods(), bads()
Ich habe die Testfunktion als erstes Argument gewählt, um die Teilanwendung in der Zukunft zu erleichtern (ähnlich wie bei der map
y filter
haben die Testfunktion als erstes Argument).