Function chaining in Python

前端 未结 6 1522
醉酒成梦
醉酒成梦 2020-11-27 11:17

On Codewars.com I encountered the following task:

Create a function add that adds numbers together when called in succession. So add(1

6条回答
  •  旧时难觅i
    2020-11-27 11:50

    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__:

    The first way would be with a custom int subclass that defines __call__ which returns a new instance of itself with the updated value:

    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
    

提交回复
热议问题