Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ignore unpacked parts of a tuple as argument of a lambda?

In Python, by convention, the underscore (_) is often used to throw away parts of an unpacked tuple, like so

>>> tup = (1,2,3)
>>> meaningfulVariableName,_,_ = tup
>>> meaningfulVariableName
1

I'm trying to do the same for a tuple argument of a lambda. It seems unfair that it can only be done with 2-tuples...

>>> map(lambda (meaningfulVariableName,_): meaningfulVariableName*2, [(1,10), (2,20), (3,30)]) # This is fine
[2, 4, 6]

>>> map(lambda (meaningfulVariableName,_,_): meaningfulVariableName*2, [(1,10,100), (2,20,200), (3,30,300)]) # But I need this!
SyntaxError: duplicate argument '_' in function definition (<pyshell#24>, line 1)

Any ideas why, and what the best way to achieve this is?

like image 595
Julian Avatar asked Dec 04 '17 21:12

Julian


People also ask

How do you pass a tuple as an argument to a function?

The use of the asterisk * in the call to the function unpacks the tuple and passes its items as positional arguments to the function.

Is it possible to unpack tuple items using for loop?

In a for-loop it works similarly. If each element of the iterable is a tuple , then you can specify two variables, and each element in the loop will be unpacked to the two. The enumerate function creates an iterable of tuples, so it can be used this way.

Can tuple be unpacked?

In python tuples can be unpacked using a function in function tuple is passed and in function values are unpacked into normal variable.

How does tuple unpacking work in Python?

When we put tuples on both sides of an assignment operator, a tuple unpacking operation takes place. The values on the right are assigned to the variables on the left according to their relative position in each tuple . As you can see in the above example, a will be 1 , b will be 2 , and c will be 3 .


2 Answers

As it is in the comments, just use stared arguments to throw an remaining arguments in "_":

lambda x, *_: x*2 

If you were using these in a map statement, as Python does not map each item in a tuple to a different parameter, you could use itertools.starmap, that does that:

from itertools import starmap
result = map(lambda x, *_: x, [(0,1,2),])

But there is no equivalent to that on the key parameter to sort or sorted.

If you won't be using arguments in the middle of the tuple, just number those:

lambda x, _1, _2, _3, w: x*2 + w

If you get a complaint from some linter tool about the parameters not being used: the purpose of the linter is to suggest mor readable code. My personal preference is not to let that to be in the way of practicity, and if this happens, I just turn off the linter for that line of code, without a second thought.

Otherwise, you will really have to do the "beautiful" thing - just use good sense if it is to please you and your team, or solely to please the linter. In this case, it is to write a full fledged function, and pretend to consume the unused arguments.

def my_otherwise_lambda(x, unused_1, unused_2, w):
     """My make linter-happy docstring here"""
     unused_1, unused_2  # Use the unused variables
     return 2 * x + w

Short of having a problem with the linter, is the purpose is to have the lambda parameter readable, then habing a full-fledged function is the recomended anyway. lambda was really close of being stripped of the language in v. 3.0, in order to commit to readability.

And last, but not least, if the semantics of the value in your tuples is that meaningful, maybe you should consider using a class to hold the values in there. In that way you could just pass the instances of that class to the lambda funcion and check the values bytheir respective names.

Namedtuple is one that would work well: from collections import namedtuple

vector = namedtuple("vector", "x y z")

mydata = [(1,10,100), (2,20,200), (3,30,300)]
mydata = [vector(*v) for v in mydata]

sorted_data = sorted(mydata, lambda v: v.x * 2)
like image 144
jsbueno Avatar answered Oct 20 '22 19:10

jsbueno


Tuples are immutable in Python so you won't be able to "throw away" (modify) the extraneous values.

Additionally, since you don't care about what those values are, there is absolutely no need to assign them to variables.

What I would do, is to simply index the tuple at the index you are interested in, like so:

>>> list(map(lambda x: x[0] * 2, [(1,10,100), (2,20,200), (3,30,300)]))
[2, 4, 6]

No need for *args or dummy variables.

like image 30
Ari Oppenheimer Avatar answered Oct 20 '22 19:10

Ari Oppenheimer