Python Function Chain

At codewars.com, I ran into the following task:

Create an add function that adds numbers together when called in sequence. Therefore add(1) should return 1 , add(1)(2) should return 1+2 , ...

While I am familiar with the basics of Python, I have never come across a function that can be called in such a sequence, that is, a function f(x) , which can be called f(x)(y)(z)... So far, I’m not even sure how to interpret this notation.

As a mathematician, I suspect that f(x)(y) is a function that assigns g_{x} each x and then returns g_{x}(y) and similarly for f(x)(y)(z)

If this interpretation is correct, Python will allow me to dynamically create functions that I am very interested in. I searched the Internet for the last hour, but could not find leadership in the right direction. However, since I do not know what this programming concept is called, this may not be too unexpected.

What do you call this concept and where can I find out more about it?

+68
function python
Aug 19 '16 at 11:48
source share
4 answers

I don’t know if this chain of functions is as strong as the chain being called, but since the functions are calling, I think no harm has been done. Anyway, I can think of it in two ways:

Subclassing int and defining __call__ :

The first method would be with a custom subclass of int , which defines __call__ , which returns a new instance of itself with an updated value

 class CustomInt(int): def __call__(self, v): return CustomInt(self + v) 

The add function can now be defined to return an instance of CustomInt , which, as the called one, which returns the updated value of itself, can be called sequentially:

 >>> def add(v): ... return CustomInt(v) >>> add(1) 1 >>> add(1)(2) 3 >>> add(1)(2)(3)(44) # and so on.. 50 

In addition, as a subclass of int return value preserves the behavior of __repr__ and __str__ int s. However, for more complex operations, you must define other passes accordingly.

As @Caridorc noted in a comment, add can also simply be written as:

 add = CustomInt 

Renaming the class to add instead of CustomInt also works the same way.




Define a closure, an additional call is required to get the value:

The only other way I can think of involves a nested function that requires an additional empty argument call to return the result. I am not using nonlocal and prefer to attach attributes to function objects to make it portable between Pythons:

 def add(v): def _inner_adder(val=None): """ if val is None we return _inner_adder.v else we increment and return ourselves """ if val is None: return _inner_adder.v _inner_adder.v += val return _inner_adder _inner_adder.v = v # save value return _inner_adder 

This continuously returns itself ( _inner_adder ), which, if val supplied, increments it ( _inner_adder += val ), and if not, returns the value as is. As I mentioned, an extra call () is required to return an extra value:

 >>> add(1)(2)() 3 >>> add(1)(2)(3)() # and so on.. 6 
+81
Aug 19 '16 at 11:53 on
source share

You can hate me, but here is one line :)

 add = lambda v: type("", (int,), {"__call__": lambda self, v: self.__class__(self + v)})(v) 

Edit: Alright, how does it work? The code is identical to @Jim's answer, but everything happens on the same line.

  • type can be used to build new types: type(name, bases, dict) -> a new type . For name we provide an empty string, since in this case the name is not required. For bases (tuple) we provide (int,) , which is identical to int inheritance. dict - class attributes to which we attach __call__ lambda.
  • self.__class__(self + v) is identical to return CustomInt(self + v)
  • A new type is created and returned inside the outer lambda.
+17
Aug 19 '16 at 12:54 on
source share

If you want to define a function that will be called several times, first you need to return the called object (for example, a function) each time, otherwise you need to create your own object by specifying the __call__ attribute so that it is accessible for calling.

The next point is that you need to save all the arguments, which in this case mean that you can use Coroutines or a recursive function. But note that Coroutines are much more optimized / flexible than recursive functions , especially for such tasks.

Here is an example function using Coroutines that saves the most recent state. Note that it cannot be called multiple times, since the return value is an integer that cannot be called, but you might consider calling the expected object; -).

 def add(): current = yield while True: value = yield current current = value + current it = add() next(it) print(it.send(10)) print(it.send(2)) print(it.send(4)) 10 12 16 
+15
Aug 19 '16 at 11:57
source share

The pythonic way to do this is to use dynamic arguments:

 def add(*args): return sum(args) 

This is not the answer you are looking for, and you may know, but I thought I would give it anyway, because if someone wonders about it not out of curiosity, but for work. They probably should have the "right thing."

+4
Aug 25 '16 at 7:33
source share



All Articles