Can lambda work with *args as its parameter? [dupl

2019-06-22 16:05发布

This question already has an answer here:

I am calculating a sum using lambda like this:

def my_func(*args):
    return reduce((lambda x, y: x + y), args)

my_func(1,2,3,4)

and its output is 10.

But I want a lambda function that takes random arguments and sums all of them. Suppose this is a lambda function:

add = lambda *args://code for adding all of args

someone should be able to call the add function as:

add(5)(10)          # it should output 15
add(1)(15)(20)(4)   # it should output 40

That is, one should be able to supply arbitrary number of parenthesis.

Is this possible in Python?

标签: python lambda
3条回答
ら.Afraid
2楼-- · 2019-06-22 16:52

You can kind of do this with a class, but I really wouldn't advise using this "party trick" in real code.

class add(object):
    def __init__(self, arg):
        self.arg = arg

    def __call__(self, arg):
        self.arg += arg
        return self

    def __repr__(self):
        return repr(self.arg)

# Test
print(add(1)(15)(20)(4))    

output

40 

Initially, add(1) creates an add instance, setting its .args attribute to 1. add(1)(15) invokes the .call method, adding 15 to the current value of .args and returning the instance so we can call it again. This same process is repeated for the subsequent calls. Finally, when the instance is passed to print its __repr__ method is invoked, which passes the string representation of .args back to print.

查看更多
干净又极端
3楼-- · 2019-06-22 16:54

The sort of "currying" you're looking for is not possible.

Imagine that add(5)(10) is 15. In that case, add(5)(10)(20) needs to be equivalent to 15(20). But 15 is not callable, and in particular is not the same thing as the "add 15" operation.

You can certainly say lambda *args: sum(args), but you would need to pass that its arguments in the usual way: add(5,10,20,93)

[EDITED to add:] There are languages in which functions with multiple arguments are handled in this sort of way; Haskell, for instance. But those are functions with a fixed number of multiple arguments, and the whole advantage of doing it that way is that if e.g. add 3 4 is 7 then add 3 is a function that adds 3 to things -- which is exactly the behaviour you're wanting not to get, if you want something like this to take a variable number of arguments.

For a function of fixed arity you can get Haskell-ish behaviour, though the syntax doesn't work so nicely in Python, just by nesting lambdas: after add = lambda x: lambda y: x+y you can say add(3)(4) and get 7, or you can say add(3) and get a function that adds 3 to things.

[EDITED again to add:] As Ashwini Chaudhary's ingenious answer shows, you actually can kinda do what you want by arranging for add(5)(10) to be not the actual integer 15 but another object that very closely resembles 15 (and will just get displayed as 15 in most contexts). For me, this is firmly in the category of "neat tricks you should know about but never ever actually do", but if you have an application that really needs this sort of behaviour, that's one way to do it.

(Why shouldn't you do this sort of thing? Mostly because it's brittle and liable to produce unexpected results in edge cases. For instance, what happens if you ask for add(5)(10.5)? That will fail with A.C.'s approach; PM 2Ring's approach will cope OK with that but has different problems; e.g., add(2)(3)==5 will be False. The other reason to avoid this sort of thing is because it's ingenious and rather obscure, and therefore liable to confuse other people reading your code. How much this matters depends on who else will be reading your code. I should add for the avoidance of doubt that I'm quite sure A.C. and PM2R are well aware of this, and that I think their answers are very clever and elegant; I am not criticizing them but offering a warning about what to do with what they've told you.)

查看更多
狗以群分
4楼-- · 2019-06-22 17:05

This is not possible with lambda, but it is definitely possible to do this is Python.

To achieve this behaviour you can subclass int and override its __call__ method to return a new instance of the same class with updated value each time:

class Add(int):
    def __call__(self, val):
        return type(self)(self + val)

Demo:

>>> Add(5)(10)
15
>>> Add(5)(10)(15)
30
>>> Add(5)
5
# Can be used to perform other arithmetic operations as well
>>> Add(5)(10)(15) * 100
3000

If you want to support floats as well then subclass from float instead of int.

查看更多
登录 后发表回答