Function chaining in Python

De openkb
Aller à : Navigation, rechercher

Sommaire

Questions

On codewars.com I encountered the following task:

Create a function add that adds numbers together when called in succession. So add(1) should return 1, add(1)(2) should return 1+2, ...

While I m familiar with the basics of Python, I ve never encountered a function that is able to be called in such succession, i.e. a function f(x) that can be called as f(x)(y)(z).... Thus far, I m not even sure how to interpret this notation.

As a mathematician, I d suspect that f(x)(y) is a function that assigns to every x a function g_{x} and then returns g_{x}(y) and likewise for f(x)(y)(z).

Should this interpretation be correct, Python would allow me to dynamically create functions which seems very interesting to me. I ve searched the web for the past hour, but wasn t able to find a lead in the right direction. Since I don t know how this programming concept is called, however, this may not be too surprising.

How do you call this concept and where can I read more about it?

Answers

I don t know whether this is function chaining as much as it s callable chaining, but, since functions are callables I guess there s no harm done. Either way, there s two ways I can think of doing this:

Sub-classing int and defining __call__:

https://docs.python.org/3/reference/datamodel.html#object.__call__ https://docs.python.org/3/reference/datamodel.html#object.__call__

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

Function add can now be defined to return a CustomInt instance, which, as a callable that returns an updated value of itself, can be called in succession:

>>> 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 an int subclass, the returned value retains the __repr__ and __str__ behavior of ints. For more complex operations though, you should define other dunders appropriately .

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

add = CustomInt 

Renaming the class to add instead of CustomInt also works similarly.


Define a closure, requires extra call to yield value:

The only other way I can think of involves a nested function that requires an extra empty argument call in order to return the result. I m not using nonlocal and opt for attaching attributes to the 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 a val is supplied, increments it (_inner_adder += val) and if not, returns the value as it is. Like I mentioned, it requires an extra () call in order to return the incremented value:

>>> add(1)(2)()
3
>>> add(1)(2)(3)()  # and so on..
6

Source

License : cc by-sa 3.0

http://stackoverflow.com/questions/39038358/function-chaining-in-python

Related

Outils personnels
Espaces de noms

Variantes
Actions
Navigation
Outils