0

In this code the object of class Node is using a variable next which is not defined anywhere and the code is still working HOW?How is the object using a variable which is not defined in its class

class Node:
    def __init__(self, data):
        self.data = data

class LinkedList:

    # Function to initialize head
    def __init__(self):
        self.head = None

    # Function to reverse the linked list
    def reverse(self):
        prev = None
        current = self.head
        while(current is not None):
            next = current.next
            current.next = prev
            prev = current
            current = next
        self.head = prev

    # Function to insert a new node at the beginning
    def push(self, new_data):
        new_node = Node(new_data)
        new_node.next = self.head
        self.head = new_node

    # Utility function to print the linked LinkedList
    def printList(self):
        temp = self.head
        while(temp):
            print(temp.data)
            temp = temp.next



llist = LinkedList()
llist.push(20)
llist.push(4)
llist.push(15)
llist.push(85)

print( "Given Linked List")
llist.printList()
llist.reverse()
print ("\nReversed Linked List")
llist.printList()

1 Answers1

1

While in most strongly typed languages this is not possible, Python allows instance attributes to be defined even after the instance has been created and the constructor has run. As long as code does not reference an attribute before it has been defined, there is no issue. See also: Can I declare Python class fields outside the constructor method?

In this particular case the following code would produce an error:

node = Node(42)
if node.next:  # Attribute error
    print("42 is not the last node")
else:
    print("42 is the last node")

However, the only place where new node instances are created is in the push method of the LinkedList class:

def push(self, new_data):
    new_node = Node(new_data)
    new_node.next = self.head
    self.head = new_node

As you can see, the next attribute is defined immediately after the node is constructed. So in practice, every node in a linked list will have a next attribute.

Best Practice?

It is open for debate whether this coding practice is advisable. For instance, Pylint has a rule defining-attr-methods which by default will raise a warning when attributes are defined outside of __init__, __new__, setUp, or __post_init__.

Alternative

In this scenario I would certainly prefer to define the next attribute in the constructor, and give the constructor an extra, optional parameter with which next can be initialised:

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

With this change, the push method of the LinkedList class can be reduce to just:

class LinkedList:
    # ...

    def push(self, new_data):
        self.head = Node(new_data, self.head)

That looks a lot more elegant.

Unrelated, but I would also let the constructor of LinkedList accept any number of values to initialise the list with:

class LinkedList:
    def __init__(self, *values):
        self.head = None
        for value in reversed(values):
            self.push(value)

Now the main code could create a list with 4 values in one go:

llist = LinkedList(85, 15, 4, 20)
trincot
  • 317,000
  • 35
  • 244
  • 286