pythondata-structureslinked-listnonetype

Attribute error : None type object in Linked List


I tried to create a simple single-linked list as the following:

class node:
    def __init__(self, data=None):
        self.data = data
        self.next = None


class linkedlist:
    def __init__(self):
        self.head = node()

    def append(self, data):
        curr = self.head
        new_node = node(data)
        while curr.next != None:
            curr = curr.next
        curr.next = new_node

    def total(self):
        curr = self.head
        total = 0
        while curr.next != None:
            curr = curr.next
            total += 1
        return total

    def display(self):
    # added total here as well
        curr = self.head
        em = []
        total = 0
        while curr.next != None:
            curr = curr.next
            em.append(curr.data)
            total += 1
        print(f"LinkedList: {em} \n Total: {self.total()}  ")

    def look(self, index):
        if index >= self.total():
            print("ERROR: Index error" )
        curr_index = self.head
        idx = 0
        while True:
            curr_index = curr_index.next
            if idx == index:
                print(curr_index.data)
            idx += 1

Whenever I am calling the look() function, I am getting the following error:

curr_index = curr_index.next
             ^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'next'

What could this mean?

As far as I know, the curr_index is using the self.head, which is inturn using node(). The node() have does have the next attribute.

This error isn't there when I call the other functions in the class. Moreover, the other functions -- except the look() function -- returns their respective values error-free.

Where is my code going wrong?


Solution

  • In this implementation, each node point to the next node of the list, and the last one points to None, signifying that's the end of the list.

    The loop in look tries to iterate over the nodes until it gets to the indexth item and then prints it, but the loop is not terminated there. In fact, it will continue indefinitely until at some point it passes the last element, making the current item None, and then fail with this error. One approach is to explicitly terminate the loop once you've reached the desired element:

    def look(self, index):
        if index >= self.total():
            # Need to throw here, not just print
            # because printing out an error won't prevent the function from moving on
            throw ValueError("ERROR: Index error")
    
        curr_index = self.head
        idx = 0
        while True:
            curr_index = curr_index.next
            if idx == index:
                print(curr_index.data)
                return # We're done here, return
            idx += 1
    

    Having said that, checking idx == index on each iteration is superfolous. You could just advance curr_index index times explicitly:

    def look(self, index):
        if index >= self.total():
            # Need to throw here, not just print
            # because printing out an error won't prevent the function from moving on
            throw ValueError("ERROR: Index error")
    
        curr_index = self.head
        for _ in range(index):
            curr_index = curr_index.next
    
        print(curr_index.data)