fizzbuzz
fizzbuzz

Reputation: 21

Function argument concept

I am fairly new to Python and I am a bit confused that how are the l1 and l2 in the second function merge(self,l1,l2) accessing to .val? If we did not declare l1 and l2 as ListNode in the parameter of the merge function then how does it know that l1 and l2 are nodes and access to .val?

If someone can explain the concept I would really appreciate it. TIA

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        if not lists or len(lists) == 0:
            return None
        
        while len(lists) > 1:
            combinedList = []
            for i in range(0, len(lists), 2):
                
                l1 = lists[i]
                    
                l2 = lists[i+1] if (i+1) < len(lists) else None
                combinedList.append(self.merge(l1,l2))
            
            lists = combinedList
            
        return lists[0]
            
        
    def merge(self, l1, l2):
        dummy = ListNode()
        output = dummy
        
        while l1 and l2:
            if l1.val < l2.val:
                output.next = l1
                l1 = l1.next
            else:
                output.next = l2
                l2 = l2.next
            output = output.next
                
        if l1:
            output.next = l1
        elif l2:
            output.next = l2
        
        return dummy.next

Upvotes: 2

Views: 58

Answers (1)

Copperfield
Copperfield

Reputation: 8510

How does it know? simple, it doesn't.

Unless you explicitly check if the thing you pass into any given function is what you want, that function simple doesn't know what is the thing you give it too, and will simple fail with some error or give some unexpected result if it isn't the desired thing.

Python is dynamically typed language that rely heavily in the so called duck typing, which can be summed up as: if it look like a duck, quack like a duck and walk like a duck, then is a duck.

now lets take a look to a quick example:

class MyObject:
    def __init__(self, val):
        self.val = val

here we defined some object, with the characteristic of having a .val attribute

now let make a function to do something with it

def fun(item:MyObject):
    return item.val + 10
     

and quickly test it

a = MyObject(10)
print(fun(a))

and it will print 20 just as expected.

But python is a dynamic language, and what that mean? it mean that you can at run time modify object, in any way, so you can make some completely different object look like any other, like

class B:
    pass

b=B()

as you see the b object can't more different from MyObject from before, it simple doesn't have anything, no attribute whatsoever (beside the default one every class have, but I digress), so it definitively would not work with fun, and it certainly doesn't, this will give you and error

print(fun(b))
#AttributeError: 'B' object has no attribute 'val'

but you can make it work

b.val=22
print(fun(b))

that will print 32, what happens here? we simple at run time added a new attribute to our b object, now, as far as fun is concerned our B instance is the same as a MyObject because it have a .val attribute that can be added with a number so is irrelevant if it is really a MyObject or not.

ok, but what about the signature of fun it clearly specify that it want a MyObject, right? that is a type annotation or type hint, it have no effect at run time, it is for documentation purposes and to clue us the programmers and/or user of the code of what is the proper intended usage of it, and for other third party tool that use those to analyse the code statically to detect bug that otherwise might pass undetected.

Upvotes: 3

Related Questions