Home > Net >  Please help me understand how this piece of code works (I think the correct term is 'currying&#
Please help me understand how this piece of code works (I think the correct term is 'currying&#

Time:12-12

I am having a hard time understanding how this code works:

class add(int):
    
    def __call__(self, int):
        return add(self   int)

result = add(2)
print(result(4))

The int 2 seems to get stored inside result(not sure if that is the case tbh), such that when we print(result), the output is 2. When print(result(4)) is ran, the output becomes 6. My question is...why is this? Is the number 2, in fact, being stored inside result? If we instantiate the add class without an argument, the result variable will output the number 0. What is happening here under the hood??

I want to understand why calling result(2)(3)(4) outputs 9.

CodePudding user response:

What this class does, it gives integers an ability to be called as a function, so that a(b) actually means a b. Imagine one day this feature is added to the python interpreter. Then you could write 2(3) and get 5. But that 5 is still callable, so we can do 5(4) and get 9, or, chained together, 2(3)(4) => 9.

In actual python this is not possible (number literals are always int), so we have to explicitly name our class (as in add(2)(3)(4)), but the principle remains the same.

"Currying" is not what's happening here.

CodePudding user response:

add is a class that subclasses int. As such, it behaves exactly as we would expect int instances to behave. So, for example, its "default"y value is 0. That is why print(add()) is 0 (since int() is 0).

Now, we implemented the __add__ method. According to the data-model, the __add__ method allows instances of classes that implement it to be callable, ie be used to the left of (), with or without argument.

Since you allowed __call__ to accept an argument and you add this argument to the value that the instance currently represents (remember that add instances behave just like a normal int would), we can call instances of add with an argument and expect the behavior you observed.

To recap, add(2) gives us an object that represents the integer 2 (just like int(2) would) with the added functionality of being able to call it with an argument that will be added to its own value (so add(2)(3) is just 2 3).

We can add some prints to see what is going on, and it is better to not use the name int in the definition of __call__ since we don't want to shadow the type int (that the add class subclasses). Ignore the call to super().__init__() if you are not familiar with it already, it is there to allow us to print the message).

class add(int):
    def __init__(self, own_value):
        print(f"Created 'add' with own_value {own_value}")
        super().__init__()

    def __call__(self, number):
        print(f"Inside __call__ with number {number}, will return new 'add' with own_value {self   number}")
        return add(self   number)


result = add(2)
print(result(4))

outputs

Created 'add' with own_value 2
Inside __call__ with number 4, will return new 'add' with own_value 6
Created 'add' with own_value 6
6
  • Related