Search code examples
pythonpython-3.xdoubly-linked-list

AttributeError: 'NoneType' object has no attribute 'next'; looked through multiple articles with the same error


I created a simple Doubly Linked List, with an append method and a str() that is supposed to return the elements present in the list. Here is the code;

class Node:
    def __init__(self,val):
        self.next = None 
        self.prev = None 
        self.val = val
    def __str__(self):
        return self.val

class dll:
    def __init__(self):
        self.head = None 
        self.tail = None 
        self.length = 0

def append(self,val):
    new = Node(val)
    if not self.head:
        self.head = new 
        self.tail = new 
    else:
        new.prev = self.tail 
        self.tail.next = new
        self.tail = new 
    self.length += 1

def __str__(self):
    curr = self.head 
    res = str()
    if not self.head:
        return("Linked list is empty")
    while curr:
        res += str(curr.val) + ' <-> '
        curr = curr.next 
        if curr.next is not None:
            res += ' <-> '
    return res


ll = dll()
print(ll)
ll.append(10)
ll.append(30)
print(ll)

When I execute this, it returns "AttributeError: 'NoneType' object has no attribute 'next'"

I removed the condition

if curr.next is not None:
            res += ' <-> '

And it works as expected, which is quite strange. I have created lists before and this has never happened to me. Am I doing something wrong? Thanks in advance!


Solution

  • The problem is in this section of your code:

        curr = curr.next 
        if curr.next is not None:
            res += ' <-> '
    

    When curr references the last node in the list, then after curr = curr.next has been executed, curr will be None. But that makes curr.next an invalid attribute access, and this produces the error you got.

    What you really want is to check whether curr was the last node, before moving forward. So change the order of your statements:

        if curr.next is not None:
            res += ' <-> '
        curr = curr.next 
    

    This will solve the issue. Note that it makes sense to only change curr at the very end of your loop body, so that everything else in the loop body gets to work with the same curr.

    Suggestion

    I would suggest to change your __str__ method so that it doesn't have this repeated +=, each time creating a new, longer, string. It is more pythonic to use join for that purpose. I would also define __iter__ and then rely on that when defining __str__:

        def __iter__(self):
            curr = self.head
            while curr:
                yield curr.val
                curr = curr.next
            
        def __str__(self):
            return " <-> ".join(map(str, self)) if self.head else "(empty)"
    

    With this in place you do things like this:

    print(ll)
    print(*ll)
    print(list(ll))
    print(sum(ll))