Наличие ошибки атрибута

Я пытаюсь реализовать единый алгоритм поиска стоимости, используя структуру графа узлов. И это мои занятия

      class Node: 
    def __init__(self, name: str, edges:list = [] ) -> None:
        self.name = name
        self.edges = edges

class Edge: 
    def __init__(self, start_node: Node, end_node: Node, weight: int) -> None:
        self.start_node = start_node
        self.end_node = end_node
        self.weight = weight

class Graph:
    def __init__(self, nodes:list = [], edges:list = []) -> None:
        self.node_list = nodes
        self.edge_list = edges

    def add_node(self, node: Node) -> None:
        self.node_list.append(node)

    def add_edge(self,start_node:Node, end_node: Node, weight: int) -> None:
        edge = Edge(start_node, end_node, weight)
        self.edge_list.append(edge)
        start_node.edges.append(edge)
        end_node.edges.append(edge)

    def get_node(self, name: str) -> Node:
        for node in self.node_list:
            if name == node.name:
                return node
            else:
                raise CityNotFoundError(name)
            
    def get_edges(self, node:str) -> list:
        for node in self.node_list:
            return node.edges

    def get_weight(self, start: str, end: str) -> int:
        start_node = self.get_node(start)
        for edge in start_node.edges:
            if edge.end_node.name == end:
                return edge.weight

Я реализуюbuild_graph(filepath:str)->Graphфункция, которая читает файл csv и строит график:

      def build_graph(path: str) -> Graph:
    file = open(path, "r")
    next(file)
    graph = Graph()
    read_cities = list()
    for line in file:
        vars = line.strip().split(",")
        city1 = vars[0]
        city2 = vars[1]
        distance = int(vars[2])

        if city1 not in read_cities:
            node1 = Node(city1)
            read_cities.append(city1)
        if city2 not in read_cities:
            node2 = Node(city2)
            read_cities.append(city2)

        start_node = graph.get_node(city1)
        end_node = graph.get_node(city2)
        graph.add_edge(start_node, end_node, distance)

    return graph

Наконец-то это мойuniform_cost_search(graph, start, end)функция

      def uniform_cost_search(graph: Graph, start: Node, end: Node):
    
    if start not in graph.node_list:
        raise CityNotFoundError(start.name)
    if end not in graph.node_list:
        raise CityNotFoundError(end.name)
    
    frontier = PriorityQueue()
    explored = set()
    path = list()
    frontier.put((0, start, path.append(start)))

    while not frontier.empty():
        cost, current_node, node_path = frontier.get()
        
        if current_node == end:
            return node_path, cost
        
        explored.add(current_node)

        for edge in graph.get_edges(current_node.name):
            neighbor = edge.end_node
            weight = edge.weight

            if neighbor not in explored:
                total_cost = cost + weight
                path.append(neighbor)
                frontier.put((total_cost, neighbor, path))
    
    raise CityNotFoundError(end)

Когда я запускаю свой код, у меня возникает такая ошибка

      Traceback (most recent call last):
  File "c:\Users\kamil\Desktop\PythonProjects\Project-1\last.py", line 115, in <module>
    graph = build_graph(file_path)
            ^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\kamil\Desktop\PythonProjects\Project-1\last.py", line 73, in build_graph
    graph.add_edge(start_node, end_node, distance)
  File "c:\Users\kamil\Desktop\PythonProjects\Project-1\last.py", line 32, in add_edge
    start_node.edges.append(edge)
AttributeError: 'NoneType' object has no attribute 'edges'
    ^^^^^^^^^^^^^^^^

Разве start_node не имеет списка с именами ребер, который я объявил как есть? Почему это происходит?

2 ответа

Разве вам не нужно обязательно добавлять узлы в граф?

      if city1 not in read_cities:
    node1 = Node(city1)
    read_cities.append(city1)
    graph.add_node(node1)

Я не совсем уверен, чтоread_citiesэто для - вы планировали собрать узлы в список и добавить их за один раз?

изменятьif name == node.listк if name == node.name

вclass Graph: get_node

с вводом какtest_OP_2.csv:

      city1,city2,distance 
A,B,95
B,C,125 
A,C,155 
A,E,50
B,E,60
A,E,55

и моя суперпечать для отладки кода (извините, я очень медленно разбираюсь в этом и не знаю об алгоритме поиска единой стоимости с использованием структуры граф-узл-ребро):

      class Node: 
    def __init__(self, name: str, edges:list = [] ) -> None:
        self.name = name
        self.edges = edges

class Edge: 
    def __init__(self, start_node: Node, end_node: Node, weight: int) -> None:
        self.start_node = start_node
        self.end_node = end_node
        self.weight = weight

class Graph:
    def __init__(self, nodes:list = [], edges:list = []) -> None:
        self.node_list = nodes
        self.edge_list = edges

    def add_node(self, node: Node) -> None:
        self.node_list.append(node)

    def add_edge(self,start_node:Node, end_node: Node, weight: int) -> None:
        edge = Edge(start_node, end_node, weight)
        self.edge_list.append(edge)
        start_node.edges.append(edge)
        end_node.edges.append(edge)

    def get_node(self, name: str) -> Node:
        for node in self.node_list:
            
            # if name == node.list: # --> ##AttributeError: 'Node' object has no attribute 'list'
            
            if name == node.name:
                
            # if name in self.node_list : #--> AttributeError: 'NoneType' object has no attribute 'edges'
            
                return node
            else:
                # raise CityNotFoundError(name)
                
                print('error')
            
    def get_edges(self, node:str) -> list:
        for node in self.node_list:
            return node.edges
            
            # return [(i.start_node.name, i.end_node.name, i.weight) for i in node.edges]

    def get_weight(self, start: str, end: str) -> int:
        start_node = self.get_node(start)
        for edge in start_node.edges:
            if edge.end_node.name == end:
                return edge.weight

    
def build_graph(path: str) -> Graph:
    file = open(path, "r")
    next(file)
    graph = Graph()
    read_cities = list()
    for line in file:
        
        print('read_cities : ', read_cities)
        print('node_list names : ', [i.name for i in graph.node_list])
        
        var = line.strip().split(",")
        
        print(var)
        
        city1 = var[0]
        city2 = var[1]
        distance = int(var[2])
        
        print('city1 : ', city1)
        
        print('city2 : ', city2)
        
        print('distance : ', distance)
        

        
        if city1 not in read_cities:
            node1 = Node(city1)
            
            print('node1 : ', node1, type(node1), 
                  'node1.name  : ', node1.name , 'node1.edges : ',node1.edges)
            
            
            
            read_cities.append(city1)
            graph.add_node(node1)
        
           
        if city2 not in read_cities:
            node2 = Node(city2)
            
            print('node2 : ', node2, type(node2), 
                  'node2.name  : ', node2.name , 'node2.edges : ',node2.edges)
            
            
            read_cities.append(city2)
            graph.add_node(node2)

        start_node = graph.get_node(city1)
        end_node = graph.get_node(city2)

        
        print('start_node : ', start_node)
        print('end_node :', end_node)
        
        graph.add_edge(start_node, end_node, distance)

    return graph


a = build_graph('test_OP_2.csv')


print('a : ', a)

print("a.get_edges('A') : " , a.get_edges('A'))

print("a.get_weight('A', 'B')  : ", a.get_weight('A', 'B'))

print("a.get_weight('C', 'E') :  ",a.get_weight('C', 'E'))

print('a.node_list : ', [i.name for i in a.node_list])

print('a.edge_list ; ', [(i.start_node.name, i.end_node.name, i.weight) for i in a.edge_list])
print(type(a))

Я получаю на выходе:

      read_cities :  []
node_list names :  []
['A', 'B', '95']
city1 :  A
city2 :  B
distance :  95
node1 :  <__main__.Node object at 0x7f47d467a2e0> <class '__main__.Node'> node1.name  :  A node1.edges :  []
node2 :  <__main__.Node object at 0x7f47d467a370> <class '__main__.Node'> node2.name  :  B node2.edges :  []
error
start_node :  <__main__.Node object at 0x7f47d467a2e0>
end_node : <__main__.Node object at 0x7f47d467a370>
read_cities :  ['A', 'B']
node_list names :  ['A', 'B']
['B', 'C', '125']
city1 :  B
city2 :  C
distance :  125
node2 :  <__main__.Node object at 0x7f47d467a520> <class '__main__.Node'> node2.name  :  C node2.edges :  [<__main__.Edge object at 0x7f47d467a4c0>, <__main__.Edge object at 0x7f47d467a4c0>]
error
error
error
start_node :  <__main__.Node object at 0x7f47d467a370>
end_node : <__main__.Node object at 0x7f47d467a520>
read_cities :  ['A', 'B', 'C']
node_list names :  ['A', 'B', 'C']
['A', 'C', '155']
city1 :  A
city2 :  C
distance :  155
error
error
start_node :  <__main__.Node object at 0x7f47d467a2e0>
end_node : <__main__.Node object at 0x7f47d467a520>
read_cities :  ['A', 'B', 'C']
node_list names :  ['A', 'B', 'C']
['A', 'E', '50']
city1 :  A
city2 :  E
distance :  50
node2 :  <__main__.Node object at 0x7f47d467a6a0> <class '__main__.Node'> node2.name  :  E node2.edges :  [<__main__.Edge object at 0x7f47d467a4c0>, <__main__.Edge object at 0x7f47d467a4c0>, <__main__.Edge object at 0x7f47d467a5b0>, <__main__.Edge object at 0x7f47d467a5b0>, <__main__.Edge object at 0x7f47d467a610>, <__main__.Edge object at 0x7f47d467a610>]
error
error
error
start_node :  <__main__.Node object at 0x7f47d467a2e0>
end_node : <__main__.Node object at 0x7f47d467a6a0>
read_cities :  ['A', 'B', 'C', 'E']
node_list names :  ['A', 'B', 'C', 'E']
['B', 'E', '60']
city1 :  B
city2 :  E
distance :  60
error
error
error
error
start_node :  <__main__.Node object at 0x7f47d467a370>
end_node : <__main__.Node object at 0x7f47d467a6a0>
read_cities :  ['A', 'B', 'C', 'E']
node_list names :  ['A', 'B', 'C', 'E']
['A', 'E', '55']
city1 :  A
city2 :  E
distance :  55
error
error
error
start_node :  <__main__.Node object at 0x7f47d467a2e0>
end_node : <__main__.Node object at 0x7f47d467a6a0>
a :  <__main__.Graph object at 0x7f47d467a1c0>
a.get_edges('A') :  [<__main__.Edge object at 0x7f47d467a4c0>, <__main__.Edge object at 0x7f47d467a4c0>, <__main__.Edge object at 0x7f47d467a5b0>, <__main__.Edge object at 0x7f47d467a5b0>, <__main__.Edge object at 0x7f47d467a610>, <__main__.Edge object at 0x7f47d467a610>, <__main__.Edge object at 0x7f47d467a670>, <__main__.Edge object at 0x7f47d467a670>, <__main__.Edge object at 0x7f47d467a700>, <__main__.Edge object at 0x7f47d467a700>, <__main__.Edge object at 0x7f47d467a760>, <__main__.Edge object at 0x7f47d467a760>]
a.get_weight('A', 'B')  :  95
error
error
a.get_weight('C', 'E') :   50
a.node_list :  ['A', 'B', 'C', 'E']
a.edge_list ;  [('A', 'B', 95), ('B', 'C', 125), ('A', 'C', 155), ('A', 'E', 50), ('B', 'E', 60), ('A', 'E', 55)]
<class '__main__.Graph'>

Я считаю, что данноеa = build_graph('test_OP_2.csv')имея:

      a.node_list :  ['A', 'B', 'C', 'E']
a.edge_list ;  [('A', 'B', 95), ('B', 'C', 125), ('A', 'C', 155), ('A', 'E', 50), ('B', 'E', 60), ('A', 'E', 55)]

это ожидаемый результат, я надеюсь.

Необходимо отметить, что алгоритм испорчен моими входными данными и принимает дубликаты ребер с разными значениями.

Также измененоvarsкvaras не рекомендуется использовать встроенные функции в качестве имени переменной.

ОТРЕДАКТИРОВАНО:

Я понял, что Graph.get_node() не вызывает исключения, если запрошенный узел отсутствует в графе, поэтому я попытался переформулировать метод так:

      def get_node(self, name: str) -> Node:
        
        node = next((x for x in self.node_list if x.name == name), None)    
        
        if node != None :
            
            return node
        
        else:
            
            raise CityNotFoundError(name)
Другие вопросы по тегам