Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Is there syntax-level support for unpacking, from tuples, the arguments to an *anonymous* function?

Suppose we have the following:

args = (4,7,5)
def foo(a,b,c): return a*b%c

Python conveniently allows tuple unpacking:

foo(4,7,5)             # returns 3
foo(*args)             # returns foo(4,7,5), i.e. 3

So that we don't have to do this:

foo(t[0], t[1], t[2])  # a repulsive, verbose, and error-prone synonym

Now suppose we had a list of similar 3-tuples and wanted a list of foo(t) for each tuple t. There is "one obvious way to do it":

list(map(lambda t: foo(*t), listoftuples))

But now suppose foo is just a throw-away function. We don't want rubbish polluting our namespace. Let's sweep it under the rug of anonymity!:

list(map(lambda t: (lambda a, b, c: a*b%c)(*t), listoftuples))

Well, we now have nested lambdas. Sure, we can parse that. But we run the risk of being mistaken for a schemer who delights in constructing cryptic spells for the sole purpose of stumping those presumptuous enough to review our code.

Furthermore, this is kinda verbose for such a simple idea. This just does not seem pythonic. (In scala, the equivalent of that inner lambda is (_*_%_), assuming context allows type inference. If this was pythonic, wouldn't it be similarly concise?).

We could remove that inner lambda this way:

list(map((lambda t: t[0] * t[1] % t[2]), listoftuples))

That's shorter, but repulsive. I have found that using magic numbers (rather than names) to refer to parameters tends to cause errors.

It would be great if it looked much more like this:

list(map((lambda a, b, c: a*b%c), listoftuples))

Of course, it couldn't be that. That's like trying to call foo(args). We need an asterisk, so to speak. Here's one possible asterisk:

def unpackInto(func): return lambda t: func(*t)

It makes for pleasantly readable code:

list(map(unpackInto(lambda a, b, c: a*b%c), listoftuples))

But we'd have to import that from a personal module all the time. That's not suitable for collaboration, and it's kind of annoying for one-time use.

TL;DR

I want unpackInto to be part of the language. Is it already supported in syntax? In standard libraries?

like image 235
stewSquared Avatar asked Dec 12 '22 08:12

stewSquared


2 Answers

In python2 it was possible to use tuple-unpacking for this:

>>> def func((a,b,c)):
...     return a+b+c
... 
>>> func((1,2,3))
6

However this feature was removed in python3. See PEP 3113. The reasons why it was removed are:

  • They are very difficult to introspect
  • They don't introduce any functionality since you can create a function like:

    def func(a_b_c):
       a,b,c = a_b_c
    

    And achieve the same result

  • They provided ugly error messages

With this removal python currently does not support what you want with any syntax nor any stdlib function.

Since this syntax was removed with a PEP I highly doubt that the core developers will accept your unpack_into function. However there may be a small chance to add something like that into the functools module which should contain this kind of things. You should probably ask to the python developers about this, but be sure to provide good arguments to support your request.

like image 193
Bakuriu Avatar answered Apr 08 '23 02:04

Bakuriu


starmap! It's in the itertools package.

From my examples:

list(map(lambda t: foo(*t), listoftuples))

becomes

list(starmap(foo, listoftuples))

See why it's called starmap? And with anonymous functions:

def unpackInto(func): return lambda t: func(*t)
list(map(unpackInto(lambda a, b, c: a*b%c), listoftuples))

becomes

list(starmap(lambda a, b, c: a*b%c, listoftuples))

A simple four-letter prefix, star, is the "asterisk" I was looking for.

So, yes, there is standard library support for unpacking parameters from tuples into anonymous functions but only for map, via starmap.

No, there is no syntax-level support, but there was syntax level support in python 2. See Bakuriu's answer. The last would become:

list(map(lambda (a, b, c): a*b%c, listoftuples))

Which is even more concise. It's almost a shame it had to be taken away.

Then again, lambda is rarely as concise or readable as a list comprehension:

[a*b%c for a, b, c in listoftuples]
like image 36
stewSquared Avatar answered Apr 08 '23 04:04

stewSquared