create Linkedlist from list

Question:

Can anyone help me understand why I get this error ?

I design a Linkedlist Class like this where I want to create a linkedlist from an array:

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

class LinkedList:
    def __init__(self, head=None):
        self.head = head

    def arrayToLL(X):
        '''Create LL from a list'''
        if not X:
            return LinkedList()
        ll = LinkedList(Node(X[0]))
        last = ll.head
        for i in range(1, len(X)):
            last.next = Node(X[i])
            last = last.next
        return ll

    def __str__(self) -> str:
        linked_list = ''
        node = self.head
        while node is not None:
            linked_list += str(node.val)
            node = node.next
            if node:
                linked_list += ' -> '
        return linked_list

When I do the implementation as I learnt for classes.
I instantiate the class first, then apply its methods

ll = LinkedList()
ll.arrayToLL([1, 2 , 3])
print(ll)

I got this Error: arrayToLL() takes 1 positional argument but 2 were given

** But when I add the ‘self’ argument to the arrayToLL(self, X) method:
and do the previous implementation I got a blank line.
Can anyone tell me why the LL wasn’t printed?? ‘1 -> 2 -> 3’

ll = LinkedList()
ll.arrayToLL([1, 2 , 3])
print(ll)

#Blank line as result

** Another thing that confuses me is that: when I call the function arrayToLL(X) directly without instantiating the linkedlist and without using self on it the LL was printed: without error. Why this happen? what I can’t use ll = LinkedList() then apply ll.methods

ll = LinkedList.arrayToLL([1, 2 , 3])
print(ll)

‘1 -> 2 -> 3’

What I did: I tried to add and/or remove ‘self’ argument in arrayToLL method an see the results.

WhatI am expecting: is to understand why in 3 cases the result change!!!
For the first case without self in arrayToLL method => I got error of missing argument
In the second case where I add the ‘self’ argument I got a blank result
In the third case where I remove ‘self’ from argument and print the method directly (LinkedList.arrayToLL([1, 2 , 3])) without instantiating the ll = LinkedList() I got the result! why this happens? Thanks

Asked By: seli

||

Answers:

There’s a good article about linked lists
https://realpython.com/linked-lists-python/

That should help you gain some understanding

Answered By: EvilSmurf

This code does the trick:

class Node:
    def __init__(self, val, next_node=None):
        self.val = val
        self.next = next_node


class LinkedList:
    def __init__(self, head=None):
        self.head = head

    def array_to_ll(self, X):
        if not X:
            return self
        self.head = Node(X[0])
        curr_last = self.head
        for elt in X:
            curr_last.next = Node(elt)
            curr_last = curr_last.next
        return self

    def __str__(self):
        ll = ""
        node = self.head
        while node is not None:
            ll += str(node.val)
            node = node.next
            if node:
                ll += ' -> '
        return ll


if __name__ == "__main__":
    ll = LinkedList()
    ll.array_to_ll([1, 2, 3])
    print(ll)

Here, you missed the self parameter of the array_to_ll method. This parameter is filled by the object you called the method with, i.e. in ll.array_to_ll([1, 2, 3]), the self parameter is filled with ll, and the X parameter is filled by [1, 2, 3]. (In fact it is the first parameter that is filled with the object, whatever its name, though it is a strong convention that this first parameter referring to the object the method is called upon should be named self in Python). This also explains the error takes 1 positional argument, but 2 were given as you gave ll and [1, 2, 3], but the method only expected X.

As a side-note, you don’t have to return anything with array_to_ll if there is no other uses for this method than to "load" your linked list. (In fact, the returned object in your code and mine is not stored in a variable, so it is lost. This does not matter as we already have a variable which stores it: ll). We could then rewrite it:

def array_to_ll(self, X):
    if not X:
        return    # This is only here to exit the function in case X is None
    self.head = Node(X[0])
    curr_last = self.head
    for elt in X:
        curr_last.next = Node(elt)
        curr_last = curr_last.next

EDIT: @trincot’s answer followed the other path and is also interesting to look into. While I have made this method depend on self (i.e. the instance) (which may or may not be pertinent), he made it static (i.e. depend on the class).

Answered By: GregoirePelegrin

I got this Error: arrayToLL() takes 1 positional argument but 2 were given

This is expected, because ll is passed as first argument, and your method did not define a parameter for it (usually called self)

But when I add the self argument to the arrayToLL(self, X) method: and do the previous implementation I got a blank line.

That happens because self is not used by that method, so ll is still empty after the call. Instead the method returns the built linked list, but then your main code is not doing anything with the returned object.

when I call the function arrayToLL(X) directly without instantiating the linkedlist and without using self on it the LL was printed: without error.

This is how it should be done, because your method is not really doing anything that relates to a self. It should be called as a static method, and it would make sense to declare it that way:

    @staticmethod
    def arrayToLL(X):
        '''Create LL from a list'''
        if not X:
            return LinkedList()
        ll = LinkedList(Node(X[0]))
        last = ll.head
        for i in range(1, len(X)):
            last.next = Node(X[i])
            last = last.next
        return ll

I see that you have made a constructor that can accept a head node as argument. I would not do that. Instead you could use the argument list to pass values for the linked list, and this would make the arrayToLL method obsolete. Furthermore, it is beneficial to add an __iter__ method to your class, which the __str__ method can use to do its job, but which can also serve in other scenarios.

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

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

    def __iter__(self):
        node = self.head
        while node:
            yield node.val
            node = node.next
    
    def __str__(self) -> str:
        return " -> ".join(map(str, self))


ll = LinkedList(1, 2, 3)
print(ll)
Answered By: trincot