graphlib — Функциональность для работы с графоподобными структурами
Исходный код: Lib/graphlib.py
-
class graphlib.TopologicalSorter(graph=None) -
Предоставляет функциональность для топологической сортировки графа с хешируемыми узлами.
Топологический порядок — это линейный порядок вершин графа, при котором для каждого направленного ребра u -> v из вершины u в вершину v вершина u располагается раньше вершины v. Например, вершины графа могут обозначать задачи, которые необходимо выполнить, а рёбра — ограничения, согласно которым одна задача должна быть выполнена раньше другой; в этом случае топологический порядок представляет собой допустимую последовательность задач. Полный топологический порядок существует тогда и только тогда, когда в графе нет направленных циклов, то есть когда он является ориентированным ациклическим графом.
Если указан необязательный аргумент graph, он должен быть словарём, представляющим ориентированный ациклический граф, в котором ключи — это узлы, а значения — итерируемые объекты со всеми предшественниками соответствующего узла в графе (узлами, из которых рёбра ведут к узлу, указанному в ключе). Дополнительные узлы можно добавить в граф с помощью метода
add().В общем случае для сортировки заданного графа необходимо выполнить следующие шаги:
- Создать экземпляр
TopologicalSorter, при необходимости передав начальный граф. - Добавить в граф дополнительные узлы.
- Вызвать для графа
prepare(). - Пока
is_active()возвращаетTrue, перебирать узлы, возвращаемые методомget_ready(), и обрабатывать их. По завершении обработки каждого узла вызывать для негоdone().
Если требуется лишь немедленно отсортировать узлы графа и параллельная обработка не нужна, можно напрямую использовать удобный метод
TopologicalSorter.static_order():>>> graph = {"D": {"B", "C"}, "C": {"A"}, "B": {"A"}} >>> ts = TopologicalSorter(graph) >>> tuple(ts.static_order()) ('A', 'C', 'B', 'D')Класс разработан для удобной поддержки параллельной обработки узлов по мере их готовности. Например:
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) -
Добавляет в граф новый узел и его предшественников. И node, и все элементы в predecessors должны быть хешируемыми.
Если метод вызывается несколько раз с одним и тем же аргументом узла, набор зависимостей будет представлять собой объединение всех переданных зависимостей.
Можно добавить узел без зависимостей (аргумент predecessors не указан) или указать зависимость дважды. Если среди predecessors указан узел, который ранее не добавляли, он будет автоматически добавлен в граф без собственных предшественников.
Вызывает
ValueError, если метод вызван послеprepare().
-
prepare() -
Завершает формирование графа и проверяет его на наличие циклов. Если обнаружен цикл, будет вызвано исключение
CycleError, однако методget_ready()по-прежнему можно использовать, чтобы получить максимально возможное количество узлов, пока циклы не блокируют дальнейший прогресс. После вызова этой функции граф нельзя изменять, поэтому добавлять узлы с помощьюadd()больше нельзя.
-
is_active() -
Возвращает
True, если можно продолжить обработку, иFalseв противном случае. Продолжить обработку можно, если циклы не препятствуют разрешению графа и при этом либо ещё есть готовые узлы, которые не были возвращены методомTopologicalSorter.get_ready(), либо число узлов, помеченных как обработанные с помощьюTopologicalSorter.done(), меньше числа узлов, возвращённых методомTopologicalSorter.get_ready().Метод класса
__bool__()делегирует вызов этой функции, поэтому вместо:if ts.is_active(): ...можно просто написать:
if ts: ...Вызывает
ValueError, если ранее не был вызван методprepare().
-
done(*nodes) -
Помечает набор узлов, возвращённых методом
TopologicalSorter.get_ready(), как обработанный, позволяя каждому преемнику узлов из nodes в дальнейшем быть возвращённым при вызове методаTopologicalSorter.get_ready().Вызывает
ValueError, если какой-либо узел из nodes уже был помечен как обработанный при предыдущем вызове этого метода, если узел не был добавлен в граф с помощьюTopologicalSorter.add(), если метод вызван без предварительного вызоваprepare()или если узел ещё не был возвращён методомget_ready().
-
get_ready() -
Возвращает
tupleсо всеми готовыми узлами. Изначально метод возвращает все узлы без предшественников, а после того как они будут помечены как обработанные вызовомTopologicalSorter.done(), последующие вызовы будут возвращать все новые узлы, предшественники которых уже обработаны. Когда дальнейший прогресс невозможен, возвращаются пустые кортежи.Вызывает
ValueError, если метод вызван без предварительного вызоваprepare().
-
static_order() -
Возвращает объект-итератор, который перебирает узлы в топологическом порядке. При использовании этого метода не следует вызывать
prepare()иdone(). Этот метод эквивалентен следующему:def static_order(self): self.prepare() while self.is_active(): node_group = self.get_ready() yield from node_group self.done(*node_group)Конкретный порядок возвращаемых узлов может зависеть от порядка их добавления в граф. Например:
>>> 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]
Это связано с тем, что «0» и «2» находятся на одном уровне графа (они были бы возвращены при одном и том же вызове
get_ready()), а порядок между ними определяется порядком добавления.Если обнаружен цикл, будет вызвано исключение
CycleError.
Добавлено в версии 3.9.
- Создать экземпляр
Исключения
Модуль graphlib определяет следующие классы исключений:
-
exception graphlib.CycleError -
Подкласс
ValueError, вызываемый методомTopologicalSorter.prepare(), если в обрабатываемом графе есть циклы. Если циклов несколько, будет выбран и включён в исключение только один из них; выбор не определён.Обнаруженный цикл можно получить из второго элемента атрибута
argsэкземпляра исключения. Он представляет собой список узлов, в котором каждый узел является непосредственным предшественником следующего узла в списке. В возвращаемом списке первый и последний узлы совпадают, чтобы показать, что список образует цикл.
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/graphlib.html