graphlib — Funktionalität zur Arbeit mit grafenähnlichen Strukturen¶
Quellcode: Lib/graphlib.py
- class graphlib.TopologicalSorter(graph=None)¶
Bietet Funktionalität zum topologischen Sortieren eines Graphen von hashbaren Knoten.
Eine topologische Reihenfolge ist eine lineare Anordnung der Knoten in einem Graphen, so dass für jede gerichtete Kante u -> v von Knoten u zu Knoten v der Knoten u in der Anordnung vor dem Knoten v kommt. Beispielsweise können die Knoten des Graphen Aufgaben darstellen, die ausgeführt werden müssen, und die Kanten können Einschränkungen darstellen, dass eine Aufgabe vor einer anderen ausgeführt werden muss; in diesem Beispiel ist eine topologische Anordnung lediglich eine gültige Reihenfolge für die Aufgaben. Eine vollständige topologische Anordnung ist genau dann möglich, wenn der Graph keine gerichteten Zyklen aufweist, d.h. wenn er ein gerichteter azyklischer Graph (DAG) ist.
Wenn das optionale Argument graph bereitgestellt wird, muss es ein Dictionary sein, das einen gerichteten azyklischen Graphen darstellt, bei dem die Schlüssel Knoten sind und die Werte Iterables aller Vorgänger dieses Knotens im Graphen sind (die Knoten, die Kanten haben, die auf den Wert im Schlüssel zeigen). Zusätzliche Knoten können dem Graphen mit der Methode
add()hinzugefügt werden.Im allgemeinen Fall sind die Schritte zur Durchführung der Sortierung eines gegebenen Graphen wie folgt:
Erstellen Sie eine Instanz von
TopologicalSortermit einem optionalen initialen Graphen.Fügen Sie dem Graphen zusätzliche Knoten hinzu.
Rufen Sie
prepare()für den Graphen auf.Solange
is_active()Trueist, iterieren Sie über die vonget_ready()zurückgegebenen Knoten und verarbeiten Sie sie. Rufen Siedone()für jeden Knoten auf, sobald dessen Verarbeitung abgeschlossen ist.
Falls nur eine sofortige Sortierung der Knoten im Graphen erforderlich ist und keine Parallelverarbeitung beteiligt ist, kann die Komfortmethode
TopologicalSorter.static_order()direkt verwendet werden.>>> graph = {"D": {"B", "C"}, "C": {"A"}, "B": {"A"}} >>> ts = TopologicalSorter(graph) >>> tuple(ts.static_order()) ('A', 'C', 'B', 'D')
Die Klasse ist so konzipiert, dass sie die Parallelverarbeitung von Knoten, sobald diese verfügbar werden, einfach unterstützt. Zum Beispiel:
topological_sorter = TopologicalSorter() # Add nodes to 'topological_sorter'... topological_sorter.prepare() while topological_sorter.is_active(): for node in topological_sorter.get_ready(): # Worker threads or processes take nodes to work on off the # 'task_queue' queue. task_queue.put(node) # When the work for a node is done, workers put the node in # 'finalized_tasks_queue' so we can get more nodes to work on. # The definition of 'is_active()' guarantees that, at this point, at # least one node has been placed on 'task_queue' that hasn't yet # been passed to 'done()', so this blocking 'get()' must (eventually) # succeed. After calling 'done()', we loop back to call 'get_ready()' # again, so put newly freed nodes on 'task_queue' as soon as # logically possible. node = finalized_tasks_queue.get() topological_sorter.done(node)
- add(node, *predecessors)¶
Fügt einen neuen Knoten und seine Vorgänger zum Graphen hinzu. Sowohl node als auch alle Elemente in predecessors müssen hashbar sein.
Wenn die Methode mehrmals mit demselben Knotenargument aufgerufen wird, ist die Menge der Abhängigkeiten die Vereinigung aller übergebenen Abhängigkeiten.
Es ist möglich, einen Knoten ohne Abhängigkeiten hinzuzufügen (predecessors wird nicht bereitgestellt) oder eine Abhängigkeit doppelt anzugeben. Wenn ein Knoten, der zuvor nicht angegeben wurde, unter predecessors enthalten ist, wird er automatisch zum Graphen hinzugefügt, ohne eigene Vorgänger.
Löst
ValueErroraus, wenn sie nach dem Aufruf vonprepare()aufgerufen wird.
- prepare()¶
Markiert den Graphen als abgeschlossen und prüft auf Zyklen im Graphen. Wenn ein Zyklus erkannt wird, wird
CycleErrorausgelöst, aberget_ready()kann immer noch verwendet werden, um so viele Knoten wie möglich zu erhalten, bis Zyklen weiteren Fortschritt blockieren. Nach einem Aufruf dieser Funktion kann der Graph nicht mehr geändert werden, und daher können keine weiteren Knoten mitadd()hinzugefügt werden.Ein
ValueErrorwird ausgelöst, wenn die Sortierung durchstatic_order()oderget_ready()gestartet wurde.Geändert in Version 3.14:
prepare()kann jetzt mehr als einmal aufgerufen werden, solange die Sortierung nicht begonnen hat. Zuvor löste diesValueErroraus.
- is_active()¶
Gibt
Truezurück, wenn weiterer Fortschritt erzielt werden kann, undFalseandernfalls. Fortschritt kann erzielt werden, wenn Zyklen die Auflösung nicht blockieren und entweder noch Knoten bereit sind, die noch nicht vonTopologicalSorter.get_ready()zurückgegeben wurden, oder die Anzahl der alsTopologicalSorter.done()markierten Knoten kleiner ist als die Anzahl, die vonTopologicalSorter.get_ready()zurückgegeben wurde.Die Methode
__bool__()dieser Klasse leitet zu dieser Funktion um, so dass anstelle vonif ts.is_active(): ...
kann einfach Folgendes getan werden:
if ts: ...
Löst
ValueErroraus, wenn sie ohne vorherigen Aufruf vonprepare()aufgerufen wird.
- done(*nodes)¶
Markiert eine Menge von Knoten, die von
TopologicalSorter.get_ready()zurückgegeben wurden, als verarbeitet und gibt damit alle Nachfolger jedes Knotens in nodes frei, damit sie zukünftig von einem Aufruf vonTopologicalSorter.get_ready()zurückgegeben werden können.Löst
ValueErroraus, wenn ein Knoten in nodes bereits durch einen früheren Aufruf dieser Methode als verarbeitet markiert wurde oder wenn ein Knoten nicht mitTopologicalSorter.add()zum Graphen hinzugefügt wurde, wenn die Methode ohne Aufruf vonprepare()aufgerufen wurde oder wenn der Knoten noch nicht vonget_ready()zurückgegeben wurde.
- get_ready()¶
Gibt ein
tuplemit allen Knoten zurück, die bereit sind. Anfänglich gibt sie alle Knoten ohne Vorgänger zurück, und sobald diese durch Aufruf vonTopologicalSorter.done()als verarbeitet markiert sind, geben nachfolgende Aufrufe alle neuen Knoten zurück, deren Vorgänger bereits verarbeitet sind. Sobald kein weiterer Fortschritt mehr möglich ist, werden leere Tupel zurückgegeben.Löst
ValueErroraus, wenn sie ohne vorherigen Aufruf vonprepare()aufgerufen wird.
- static_order()¶
Gibt ein Iteratorobjekt zurück, das über die Knoten in topologischer Reihenfolge iteriert. Bei Verwendung dieser Methode sollten
prepare()unddone()nicht aufgerufen werden. Diese Methode ist äquivalent zudef static_order(self): self.prepare() while self.is_active(): node_group = self.get_ready() yield from node_group self.done(*node_group)
Die spezifische Reihenfolge, die zurückgegeben wird, kann von der spezifischen Reihenfolge abhängen, in der die Elemente in den Graphen eingefügt wurden. Zum Beispiel:
>>> ts = TopologicalSorter() >>> ts.add(3, 2, 1) >>> ts.add(1, 0) >>> print([*ts.static_order()]) [2, 0, 1, 3] >>> ts2 = TopologicalSorter() >>> ts2.add(1, 0) >>> ts2.add(3, 2, 1) >>> print([*ts2.static_order()]) [0, 2, 1, 3]
Dies liegt daran, dass "0" und "2" auf derselben Ebene im Graphen liegen (sie wären im selben Aufruf von
get_ready()zurückgegeben worden) und die Reihenfolge zwischen ihnen durch die Einfügungsreihenfolge bestimmt wird.Wenn ein Zyklus erkannt wird, wird
CycleErrorausgelöst.
Hinzugefügt in Version 3.9.
Ausnahmen¶
Das Modul graphlib definiert die folgenden Ausnahmeklassen:
- exception graphlib.CycleError¶
Unterklasse von
ValueError, ausgelöst vonTopologicalSorter.prepare(), wenn Zyklen im Arbeitsgraphen vorhanden sind. Wenn mehrere Zyklen vorhanden sind, wird nur eine undefinierte Wahl unter ihnen gemeldet und in die Ausnahme aufgenommen.Der erkannte Zyklus kann über das zweite Element des Attributs
argsder Ausnahmeinstanz abgerufen werden und besteht aus einer Liste von Knoten, so dass jeder Knoten im Graphen ein direkter Vorgänger des nächsten Knotens in der Liste ist. In der gemeldeten Liste sind der erste und der letzte Knoten gleich, um klarzustellen, dass es sich um einen Zyklus handelt.