Valeria
Valeria

Reputation: 1362

"Unresolved attribute reference" in PyCharm

I get an unresolved attribute reference 'get_opposite_node' for class 'list' warning in PyCharm, even though the code works fine.

This the relevant part of the code:

    def bfs(self, start, end):
        visited = {start}  # visited set contains int indices
        start_node = self.Node(start)
        q = deque()
        q.append(start_node)  # deque contains Node objects
        while q:
            cur_node = q.popleft()  # Node object
            if cur_node.idx == end:
                return cur_node 

            for edge in self.get_out_edges(cur_node):
                # HERE IS WHERE I GET THE WARNING:
                child = self.Node(edge.get_opposite_node(i))
                child_idx = child.idx
                if child_idx not in visited:
                    visited.add(child_idx)
                    q.append(child)
                    child.parent = cur_node
        return None

    def get_out_edges(self, node):
        return self.adj[node.idx]

    def add_edge(self, line):
        # line is just a string from the input file 
        (v, u, w) = [int(x) for x in line.split()]
        e = Edge(v,u,w)
        self.adj[v].append(e)
        self.adj[u].append(e)
        self.edges.append(e)

self.adj is the list of lists:self.adj = [[] for _ in range(N)], which is later filled by the edges of class Edge:

class Edge:
    def __init__(self, v, u, w):
        self.v = v
        self.u = u
        self.w = w

    def get_node(self):
        return self.v

    def get_opposite_node(self, k=None):
        return self.u if k == self.v or k is None else self.v

So, basically, while self.adj[i] contains Edges, PyCharm thinks that it contains lists(?). Is there a way to 'convince' PyCharm that it is not like this?

Upvotes: 3

Views: 12533

Answers (1)

Rob Bricheno
Rob Bricheno

Reputation: 4653

PyCharm is confused because you have explicitly declared adj to be a list of (empty) lists. It can't tell that you've subsequently overwritten each element of adj with an object which understands get_opposite_node(). But it does know that you can't get_opposite_node() on a list.

You could work around this by declaring adj to contain a list of None instead of a list of empty lists. The below may clarify a little:

class RealEdge:
    def get_something(self):
        return "something"


class SomeEdgyThing:
    def __init__(self, size):
        self.adj = [[] for _ in range(size)]


class SomeOtherEdgyThing:
    def __init__(self, size):
        self.adj = [None for _ in range(size)]


class ReallyEdgyThing:
    def __init__(self, size):
        self.adj = [RealEdge() for _ in range(size)]


my_complicated_thing = SomeEdgyThing(7)
for i in range(len(my_complicated_thing.adj)):
    my_complicated_thing.adj[i] = RealEdge()
# PyCharm warning:
#     Unresolved attribute reference 'get_something' for class 'list'
# Runs fine, because we overwrote all the values in adj
# with RealEdges instead of lists.
print(my_complicated_thing.adj[1].get_something())

my_thing = SomeEdgyThing(7)
try:
    # PyCharm warning:
    #     Unresolved attribute reference 'get_something' for class 'list'
    # Also a runtime error:
    #     AttributeError: 'list' object has no attribute 'get_something'
    print(my_thing.adj[1].get_something())
except AttributeError as e:
    print(e)

my_other_thing = SomeOtherEdgyThing(7)
try:
    # PyCharm doesn't complain.
    # Does it assume NoneType is part of a union type...?
    # Runtime error:
    #     AttributeError: 'NoneType' object has no attribute 'get_something'
    print(my_other_thing.adj[1].get_something())
except AttributeError as e:
    print(e)

real_thing = ReallyEdgyThing(7)
# No error
print(real_thing.adj[1].get_something())

Output:

something
'list' object has no attribute 'get_something'
'NoneType' object has no attribute 'get_something'
something

I suspect that you don't really need to write self.adj = [[] for _ in range(N)] and that self.adj = [None for _ in range(N)] will serve just as well and will cause the warning to disappear.

Now, you are still exposed to the case where your code runs in the wrong order somehow and you try to call get_opposite_node() on a member of adj that you haven't updated to contain a real Edge. You just aren't being warned about it any more. For that reason I would recommend wrapping these calls inside a try: ... except AttributeError: block.

Upvotes: 3

Related Questions