50

If I have a function/method that is an implementation of a callback for some framework, and do not care for any further arguments, it seems to be syntactically correct, and to not have pylint/IDE complaints to use *_ to express no interest in any further arguments. The point I think is to express intent to both the tools, and other developers that these arguments are not currently relevant.

To clarify what I mean:

def my_callbacK_handler(a, b, *_):
    ...

I've not seen this idiom used in the wild - is it common, are there examples and are there known problems with this?

For those not familiar: _ expresses the intent that I am not interested in that symbol - it is the Python "dummy" that is recognized by IDE's and linters.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Danny Staple
  • 7,101
  • 4
  • 43
  • 56

4 Answers4

49

_ is just a variable like any other, but by convention it means that you don't intend to use that value, just declare and forget it.

[OrderedSet() for _ in xrange(n)]

builds a list of n empty ordered sets. _ is the index, but is unused; _ is commonly used as required, but unused, variable name (not only in Python). This is just a tricky Python idiom because there is no built-in syntax to do this.

It's not uncommon to see this in other languages (where _ is a valid identifier). _ often means a variable one's not interested in the value of, but which is needed for syntactic reasons.

Note that _(...) as a macro call has another conventional meaning, which comes from gettext, where one uses _("string literal") to indicate a string that needs localization.

A thread to read from ActivesState

I can not say about any problems, Python doesn't use it internally, it's just a variable, it is us who have to be careful.


Regarding the syntax of * and **: The names *args and **kwargs are only by convention, but there's no need not to use them.

def my_callbacK_handler(a, b, *_):
    ...

So what I understand if I see this function in your code by _ idiom, that this function only uses a and b arguments in its working and will ignore others.

As you state: "*_ to express no interest in any further arguments".

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Rahul Gautam
  • 4,749
  • 2
  • 21
  • 30
  • 7
    That is more an explaination of the meaning of `_` than where it is used in the `*_` idiom. A good explanation, but not quite answering the intended question. – Danny Staple Dec 05 '12 at 16:05
  • 2
    Ok Thanks Danny, check out the update if i understand you correctly. I don't see any problem using *_ its just depend on your perspective to see things. some times we have our own idioms in our project its something like that. – Rahul Gautam Dec 06 '12 at 06:42
  • 1
    Yes - that update makes sense - the answer explains for those not using *args and **kwargs or the underscore, and hopefully the "I am not interested in any further arguments" expression is clear to other coders reading this, and not just the IDE and linters. – Danny Staple Dec 08 '12 at 22:50
  • This is old, but I'll chime in anyhow. I would consider this bad practice because the only time your function should be receiving arguments that it doesn't need is when those arguments are being passed on. `*_` implies it's ignoring those arguments completely. I consider than an error passing silently. For instance: `f(a, b, **_)` if I call it as `f(filename, path_name, verbose=True)` and it doesn't change verbosity, doesn't throw an error, and doesn't return anything differently. That should throw an error at me, but won't because my code is needlessly permissive. – Adam Smith Jul 13 '17 at 07:20
  • 1
    @AdamSmith It can be valid if a 3rd party is configured to call these functions and will pass on some args that may be either used or ignored. If ignored, it might make sense to do `*_` although if it's so rare that it looks strange and surprising, it may arguably be better to just do `*args` even when ignored. – Rainy Aug 23 '19 at 21:33
  • @Rainy How do you imagine that working? If they are used, then they should be referenced in the function or handed off to a delegated function so they need a name. If they are ignored, you should not allow the caller to pass them. The only use I've seen of this is to stub a "constant" function for use as a callback, but generally callback functions are given one argument -- the invoking event -- not a whole list of them.... – Adam Smith Aug 23 '19 at 23:47
  • but I agree that even in this case, you should prefer `*args` and simply not use it. Similar to how you should avoid doing `for root, _, files in os.walk(path)` – Adam Smith Aug 23 '19 at 23:47
  • 1
    @AdamSmith You don't have a choice to disallow caller to pass them, because the caller is a third party package, for example CeleryBeat part of Celery which passes args with task meta data to a function that defines a task to run. – Rainy Aug 26 '19 at 20:37
10

*_ means multiple placeholders,just like you write def my_callbacK_handler(a, b, _,_,_....,_): and will create a variable named _ which is a list of the extra arguments

It has two advantage:

  1. It can handle the case where there is no extra argument
def f(): 
   return 1,2,3 

def g(): 
   return 1,2

def h(): 
   return 1,2,3,4

_ as a placeholder can only deal with exactly one value

x,y,_ = g() # ValueError: not enough values to unpack (expected 3, got 2)

x,y,_ = h() # ValueError: too many values to unpack (expected 3)

whereas *_ can deal with any amount of values

x,y,*_ = g()                                                                                                                  

print(_)                                                                                                                      
[]

x,y,*_ = h()                                                                                                                  

print(_)                                                                                                                      
[3, 4]

One word: Prefer *_ for more robust code.

Rafael
  • 1,761
  • 1
  • 14
  • 21
1

I belive that using _ as a "don't care" variable name originates with Prolog. In Prolog it not only means that you don't care but it has a different semantics from other variables in that normally two instances of the same variable (eg Dummy) must match the same value, whereas two instances of _ can match different values.

I was surprised to see it being used in Python and confused when I first saw it. But it does make sense, and therefore so does *_ . Now I use it myself.

BTW It seems that _ does have a special semantics in Python as well but only in the context of it being used in the top level of the Python shell (I think), where _ gets automatically assigned the value of the last returned function value.

0

To better express to a fellow Python developer that you're ignoring the rest of the arguments in a function call, consider writing your callback function like this:

def my_callback_handler(a, b, *args, **kwargs):
    ...

So, by doing it like this, Python will accept any remaining positional and/or keyword arguments, so your callback function will work as it should, without any nagging that you forgot to add something.

The takeaway here is to always follow Python conventions, because those are the most widely understood by all Python developers.

Boštjan Mejak
  • 827
  • 9
  • 23
  • With this one, I'd expect linters to complain about the unused variables. Hence the `_` to say they aren't handled. The alternative would be a linter directive to ignore them. – Danny Staple May 11 '23 at 19:37