Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wildcards in Python?

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?

like image 299
Joe Avatar asked Nov 01 '11 16:11

Joe


2 Answers

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.

like image 25
wisty Avatar answered Sep 24 '22 21:09

wisty


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.

like image 165
Sven Marnach Avatar answered Sep 25 '22 21:09

Sven Marnach