diff --git a/mod_oop/4.8_01_linked_graph.py b/mod_oop/4.8_01_linked_graph.py index 1577cf5..1bef6e2 100644 --- a/mod_oop/4.8_01_linked_graph.py +++ b/mod_oop/4.8_01_linked_graph.py @@ -12,7 +12,8 @@ Link - для описания связи между двумя произвол LinkedGraph - для представления связного графа в целом (карта целиком). Объекты класса Vertex должны создаваться командой: -v = Vertex() +>>> v = Vertex() + и содержать локальный атрибут: _links - список связей с другими вершинами графа (список объектов класса Link). @@ -20,7 +21,9 @@ _links - список связей с другими вершинами граф links - для получения ссылки на список _links. Объекты следующего класса Link должны создаваться командой: -link = Link(v1, v2) +>>> v1 = Vertex(); v2 = Vertex() +>>> link = Link(v1, v2) + где v1, v2 - объекты класса Vertex (вершины графа). Внутри каждого объекта класса Link должны формироваться следующие локальные атрибуты: _v1, _v2 - ссылки на объекты класса Vertex, которые соединяются данной связью; @@ -32,7 +35,7 @@ v2 - для получения ссылки на вершину v2; dist - для изменения и считывания значения атрибута _dist. Наконец, объекты третьего класса LinkedGraph должны создаваться командой: -map_graph = LinkedGraph() +>>> map_graph = LinkedGraph() В каждом объекте класса LinkedGraph должны формироваться локальные атрибуты: _links - список из всех связей графа (из объектов класса Link); @@ -58,30 +61,29 @@ _links = [Link(v1, v2)] Пример использования классов, применительно к схеме метро (эти строчки в программе писать не нужно): -map_graph = LinkedGraph() - -v1 = Vertex() -v2 = Vertex() -v3 = Vertex() -v4 = Vertex() -v5 = Vertex() -v6 = Vertex() -v7 = Vertex() - -map_graph.add_link(Link(v1, v2)) -map_graph.add_link(Link(v2, v3)) -map_graph.add_link(Link(v1, v3)) - -map_graph.add_link(Link(v4, v5)) -map_graph.add_link(Link(v6, v7)) - -map_graph.add_link(Link(v2, v7)) -map_graph.add_link(Link(v3, v4)) -map_graph.add_link(Link(v5, v6)) - -print(len(map_graph._links)) # 8 связей -print(len(map_graph._vertex)) # 7 вершин -path = map_graph.find_path(v1, v6) +>>> Vertex._num = 0 +>>> map_graph = LinkedGraph() +>>> v1 = Vertex() +>>> v2 = Vertex() +>>> v3 = Vertex() +>>> v4 = Vertex() +>>> v5 = Vertex() +>>> v6 = Vertex() +>>> v7 = Vertex() +>>> map_graph.add_link(Link(v1, v2)) +>>> map_graph.add_link(Link(v2, v3)) +>>> map_graph.add_link(Link(v1, v3)) +>>> map_graph.add_link(Link(v4, v5)) +>>> map_graph.add_link(Link(v6, v7)) +>>> map_graph.add_link(Link(v2, v7)) +>>> map_graph.add_link(Link(v3, v4)) +>>> map_graph.add_link(Link(v5, v6)) +>>> len(map_graph._links) +8 +>>> len(map_graph._vertex) +7 +>>> map_graph.find_path(v1, v6) +([Vertex('A'), Vertex('B'), Vertex('G'), Vertex('F')], [Link(Vertex('A'), Vertex('B'), 1), Link(Vertex('B'), Vertex('G'), 1), Link(Vertex('F'), Vertex('G'), 1)]) Однако, в таком виде применять классы для схемы карты метро не очень удобно. Например, здесь нет указаний названий станций, а также длина каждого сегмента равна 1, что не соответствует действительности. @@ -91,7 +93,8 @@ class Station(Vertex): ... - для описания станций метро; class LinkMetro(Link): ... - для описания связей между станциями метро. Объекты класса Station должны создаваться командой: -st = Station(name) +>>> st = Station(name := "Домодедовская") + где name - название станции (строка). В каждом объекте класса Station должен дополнительно формироваться локальный атрибут: name - название станции метро. @@ -99,37 +102,42 @@ name - название станции метро. В самом классе Station переопределите магические методы __str__() и __repr__(), чтобы они возвращали название станции метро (локальный атрибут name). Объекты второго класса LinkMetro должны создаваться командой: -link = LinkMetro(v1, v2, dist) +>>> link = LinkMetro(v1, v2, dist := 2) + где v1, v2 - вершины (станции метро); dist - расстояние между станциями (любое положительное число). (Также не забывайте в инициализаторе этого дочернего класса вызывать инициализатор базового класса). В результате, эти классы должны совместно работать следующим образом (эти строчки в программе писать не нужно): -map_metro = LinkedGraph() -v1 = Station("Сретенский бульвар") -v2 = Station("Тургеневская") -v3 = Station("Чистые пруды") -v4 = Station("Лубянка") -v5 = Station("Кузнецкий мост") -v6 = Station("Китай-город 1") -v7 = Station("Китай-город 2") +>>> map_metro = LinkedGraph() +>>> v1 = Station("Сретенский бульвар") +>>> v2 = Station("Тургеневская") +>>> v3 = Station("Чистые пруды") +>>> v4 = Station("Лубянка") +>>> v5 = Station("Кузнецкий мост") +>>> v6 = Station("Китай-город 1") +>>> v7 = Station("Китай-город 2") -map_metro.add_link(LinkMetro(v1, v2, 1)) -map_metro.add_link(LinkMetro(v2, v3, 1)) -map_metro.add_link(LinkMetro(v1, v3, 1)) +>>> map_metro.add_link(LinkMetro(v1, v2, 1)) +>>> map_metro.add_link(LinkMetro(v2, v3, 1)) +>>> map_metro.add_link(LinkMetro(v1, v3, 1)) -map_metro.add_link(LinkMetro(v4, v5, 1)) -map_metro.add_link(LinkMetro(v6, v7, 1)) +>>> map_metro.add_link(LinkMetro(v4, v5, 1)) +>>> map_metro.add_link(LinkMetro(v6, v7, 1)) -map_metro.add_link(LinkMetro(v2, v7, 5)) -map_metro.add_link(LinkMetro(v3, v4, 3)) -map_metro.add_link(LinkMetro(v5, v6, 3)) +>>> map_metro.add_link(LinkMetro(v2, v7, 5)) +>>> map_metro.add_link(LinkMetro(v3, v4, 3)) +>>> map_metro.add_link(LinkMetro(v5, v6, 3)) -print(len(map_metro._links)) -print(len(map_metro._vertex)) -path = map_metro.find_path(v1, v6) # от сретенского бульвара до китай-город 1 -print(path[0]) # [Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1] -print(sum([x.dist for x in path[1]])) # 7 +>>> print(len(map_metro._links)) +8 +>>> print(len(map_metro._vertex)) +7 +>>> path = map_metro.find_path(v1, v6) # от сретенского бульвара до китай-город 1 +>>> print(path[0]) # [Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1] +[Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1] +>>> print(sum([x.dist for x in path[1]])) # 7 +7 P.S. В программе нужно объявить только классы Vertex, Link, LinkedGraph, Station, LinkMetro. На экран ничего выводить не нужно. """ @@ -137,7 +145,7 @@ P.S. В программе нужно объявить только классы from collections import defaultdict from dataclasses import dataclass, field from functools import total_ordering -from typing import List, Union +from typing import List, Optional, Tuple, Union def make_properties_prot(*names): @@ -159,12 +167,14 @@ def make_properties_prot(*names): class AutoNamed: - _num = 0 + _num: int = 0 name: str @staticmethod - def column_code(num): - def gen(n): + def column_code(num: int) -> str: + """Имя как код колонки в Excel по порядковому номеру с 1""" + + def gen(n: int): a = ord("A") sz = ord("Z") - a + 1 while n: @@ -182,7 +192,7 @@ class AutoNamed: @make_properties_prot("links") class Vertex(AutoNamed): - def __init__(self, name=None): + def __init__(self, name: Optional[str] = None): if name: self.name = name self._links = [] @@ -238,7 +248,7 @@ class LinksPath: return sum([x.dist for x in self.links]) return float("inf") - def add_link(self, link): + def add_link(self, link: Link): if link not in self.links: self.links.append(link) return self @@ -263,52 +273,51 @@ class LinksPath: @make_properties_prot("links", "vertex") class LinkedGraph: - def __init__(self, vertex=None, links=None): + def __init__(self, vertex: Optional[Vertex] = None, links: Optional[Link] = None): self._vertex = vertex or [] self._links = links or [] def __repr__(self) -> str: return f"{self.__class__.__name__}{(self.vertex, self.links)!r}" - def add_vertex(self, v): + def add_vertex(self, v: Vertex): if v not in self._vertex: self._vertex.append(v) - def add_link(self, link): + def add_link(self, link: Link): if link not in self._links: self._links.append(link) for v in link: self.add_vertex(v) - def dijkstras(self, start, stop=None): + def dijkstras(self, start: Vertex, stop: Optional[Vertex] = None): + def walk(remaining, paths, current): + while remaining: + remaining.discard(current) + yield current + if current == stop: + break + if remaining: + current = min(remaining, key=lambda x: paths[x]) + paths = defaultdict(LinksPath) paths[start].is_start = True - remaining, current = set(self.vertex), start - while remaining: - remaining.discard(current) + remaining = set(self.vertex) + for current in walk(remaining, paths, start): for link in current: - for v in link: - if v not in remaining: - continue + for v in filter(lambda v: v in remaining, link): new_path = paths[current].copy().add_link(link) - if new_path < paths[v]: - paths[v] = new_path - if current == stop: - break - if remaining: - current = min(remaining, key=lambda x: paths[x]) + paths[v] = min(paths[v], new_path) return paths - def find_path(self, start_v, stop_v): + def find_path( + self, start_v: Vertex, stop_v: Vertex + ) -> Tuple[List[Vertex], List[Link]]: path = self.dijkstras(start_v, stop_v)[stop_v] return path.vertex, path.links class Station(Vertex): - def __init__(self, name: str): - super().__init__() - self.name = name - def __repr__(self): return self.name @@ -317,14 +326,9 @@ class LinkMetro(Link): ... -vA = Vertex("A") -vB = Vertex("B") -vC = Vertex("C") -vD = Vertex("D") -vE = Vertex("E") -vF = Vertex("F") -vG = Vertex("G") +print("-------------------------------------------") +vA, vB, vC, vD, vE, vF, vG = [Vertex() for _ in range(7)] map_graph = LinkedGraph() map_graph.add_link(Link(vA, vB)) map_graph.add_link(Link(vB, vC)) @@ -340,6 +344,7 @@ map_graph.add_link(Link(vE, vF)) print(len(map_graph._links)) # 8 связей print(len(map_graph._vertex)) # 7 вершин print(map_graph.find_path(vA, vG)) +# ([Vertex('A'), Vertex('B'), Vertex('G')], [Link(Vertex('A'), Vertex('B'), 1), Link(Vertex('B'), Vertex('G'), 1)]) print("-------------------------------------------") @@ -370,6 +375,7 @@ print(path[0]) # [Сретенский бульвар, Тургеневская print(sum([x.dist for x in path[1]])) # 7 print("-------------------------------------------", flush=True) +Vertex._num = 0 # naming reset # exit(1)