Wildcards in Python?
-
18-02-2021 - |
Domanda
Over the years I have noticed the 'wildcard' variable in various bits and pieces of Python I've come across. I assumed it worked like Haskell: allowing you to put a variable where one was required in the formal parameters, but not binding it.
I've used this on, for example, the left hand side of an tuple-unpacking assignment when I don't need one of the variables.
For example:
_, extension = os.path.splitext(filename)
So when I wrote something similar to this today:
(lambda (x,_,_): x)((1,2,3))
I.E. I tried to bind the underscore twice, I received a syntax error. I was surprised to see that _ is indeed a real variable:
(lambda (x,_,z): _)((1,2,3))
> 2
Looks like _
is just a variable name like any other.
Is there a bona fide wildcard variable that I can use as I would like (i.e. able to use more than one in a tuple unpacking assignment), as per the first example?
Soluzione
There is no wildcard variable in Python.
I try to dissuade people from using _
as a variable name for quite some time now. You are not the first person mistaking _
as some kind of special syntax, so it's better not to use _
as a variable name at all to avoid this kind of confusion. If there ever was a "convention" to use _
as a throw-away variable name, this convention was misguided.
There are more problems than just the confusion it causes. For example, _
clashes with _
in the interactive interpreter and the common gettext alias.
Regarding the lambda
expression, I'd just use lambda x, *args: ...
to ignore all arguments except for the first one. In other cases, I'd use names explicitly stating I don't want to use them, like dummy
. In case of loops of range()
s, I usually use for i in range(n)
and simply don't use i
.
Edit: I just noticed (by looking at the other answers) that you use tuple unpacking in the argument list, so lambda x, *args: ...
doesn't solve your problem. Tuple unpacking in parameter lists has been removed in Python 3.x because it was considered too obscure a feature. Better go with mipadi's answer instead.
Altri suggerimenti
Not really. Python is not Haskell. Map, apply, reduce, and lambda are kind of second-class citizens, though there is some interesting stuff in itertools.
Unless you have some need to use one-line lambdas, the correct way is this:
def f(x, *args, **kwargs):
return x
The *args
argument lets you use any number of unnamed arguments (which will be available as a tuple called args
). Extra named arguments will be in a dictionary called kwargs
.
I don't think there's any way to do this in a lambda, but there's usually no need. A function declaration can go anywhere. Note, you do interesting / evil stuff if you put the function definition inside another function (or loop):
def make_func(s):
def f(*trash, **more_trash):
print s
return f
f1 = make_func('hello')
f2 = make_func('world')
f1(1,2,'ham','spam')
f2(1,2,a=1,b=2)
will output:
>>> hello
>>> world
As @rplnt pointed out, this won't be the same for loops:
funcs = []
for s in ('hello','world'):
def f():
print s
funcs.append(f)
for f in funcs:
f()
will output:
>>> world
>>> world
because loops only have one namespace.
No, Python doesn't have any equivalent to Haskell's _
. The convention in Python is to use _
for "throwaway" variables, but it's an actual variable name, and as you found, you can't use it twice in the same context (like a lambda parameter list).
In the examples you gave, I'd just use indexing:
lambda tup: tup[0]
or
lambda tup: tup[1]
Not as pretty, but one of the better ways to do it.
It is possible, with a little trick:
class _:
def __eq__(x,y): return true
_=_() #always create a new _ instance if used, the class name itself is not needed anymore
[(a,b) for (a,_,_,b) in [(1,2,3,4),(5,6,7,8)]]
gives
[(1, 4), (5, 8)]
I'm using it often, because it makes code more elegant, a part of Haskells beauty in Python.
Short answer is no. Could just follow your existing convention. That is
(lambda (x, _1, _2): x)((1,2,3))