107

When "deconstructing" a tuple, I can use _ to denote tuple elements I'm not interested in, e.g.

>>> a,_,_ = (1,2,3)
>>> a
1

Using Python 2.x, how can I express the same with function arguments? I tried to use underscores:

>>> def f(a,_,_): return a
...
  File "<stdin>", line 1
SyntaxError: duplicate argument '_' in function definition

I also tried to just omit the argument altogether:

>>> def f(a,,): return a
  File "<stdin>", line 1
    def f(a,,): return a
        ^
SyntaxError: invalid syntax

Is there another way to achieve the same?

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • 4
    Why not just have default values for your arguments? Why would you have unused arguments in a function? – jamylak Apr 05 '12 at 09:05
  • 14
    @jamylak: I use a framework which expects me to pass callables in various places. However, in plenty of cases, I don't actually need all arguments passed by the framework. – Frerich Raabe Apr 05 '12 at 09:14
  • 5
    @FrerichRaabe if the framework requires particular arguments then it would seem cleanest just to use the same names that the framework defines for each argument whether or not the function uses them. That also means you don't have to change the signature of your function when you later discover you did need the other arguments after all. – Duncan Apr 05 '12 at 09:30
  • @Duncan: That's what I do right now. It's just that some arguments have rather long names, so I considered shortening them to something - say, `_` - so that I can see that there's a variable being passed - I just happen to not need it (right now). – Frerich Raabe Apr 05 '12 at 09:56
  • @jamylak: Having default values for the arguments doesn't make them used, so you would still have the problem. – HelloGoodbye Sep 27 '16 at 12:50
  • @helloGoodbye What problem? Just put in some default values and continue life as normal eg. param_name=None. I recommend against any of these answers. It would be more clear to just have the proper argument names than have a `*unused` which looks ugly or `del unused1, unused2, unused3`. If you're forced to have these unused paramters at least give them the proper names for when they possibly get used in the future if the API is always passing them in. So the answer is simple, give them the relevant names (no messing around with `unused`) and just dont use them – jamylak Sep 28 '16 at 06:46
  • 4
    @jamylak For a fellow colleague (and tools like pylint) it's not clear though whether the variables are *intentionally* unused. So you'd end up adding some sort of comment -- I'd like to avoid that if possible by expressing this in the code itself. – Frerich Raabe Sep 28 '16 at 06:53
  • @FrerichRaabe I think the comment in function description suffices . Having `del unused1` creates more confusion. A new user reading over the code whos never read this stackoverflow will be wondering "why are there all these `del unused1, unused1`, what does that do?" and will be looking for a comment anyway to see why all this `del unused` is going on. – jamylak Sep 28 '16 at 06:59
  • 1
    @jamylak I wasn't argueing in favor of the answer which proposed to use `del` (I also don't like it myself), I was argueing against giving the arguments plausible names but then simply not using them since tools as well as colleagues will probably raise their (electronic) eyebrows over this. Of course I can always document my intentions as English text, but I'd prefer something which expresses it in Python. – Frerich Raabe Sep 28 '16 at 07:35
  • @jamylak I was referring to the warnings you get in PyCharm (and probably in other editors too) when you have unused arguments in a function. I was maybe not so clear about that, although I kind of supposed that is why Frederich asked the question. But now I see that it was because it caused problems for other software like linting tools, as well as for actual humans. – HelloGoodbye Sep 28 '16 at 20:16
  • @FrerichRaabe, something that speaks for the `del` method is that it is the only method mentioned here that works for arguments that come before other arguments that you actually want to use (except from if you are okay with reading those arguments from an [arbitrary arguments list](https://docs.python.org/3/tutorial/controlflow.html#arbitrary-argument-lists), which I would not be). I personally think `del` in combination with a comment seems like a pretty optimal universal method. – HelloGoodbye Sep 28 '16 at 20:24
  • The example on the first line a,_,_ = (1,2,3) is also equivalent to a,*_ = (1,2,3) – thanos.a May 20 '22 at 14:12

8 Answers8

148

A funny way I just thought of is to delete the variable:

def f(foo, unused1, unused2, unused3):
    del unused1, unused2, unused3
    return foo

This has numerous advantages:

  • The unused variable can still be used when calling the function both as a positional argument and as a keyword argument.
  • If you start to use it later, you can't since it's deleted, so there is less risk of mistakes.
  • It's standard python syntax.
  • PyCharm does the right thing! (As of 2020, PyCharm no longer does the right thing :( tracking this at https://youtrack.jetbrains.com/issue/PY-39889 )
  • PyLint won't complain and using del is the solution recommended in the PyLint manual.
hnwoh
  • 128
  • 1
  • 10
boxed
  • 3,895
  • 2
  • 24
  • 26
  • 2
    I very much like the tip and will adopt it. I'd still add a small comment such as `# Ignored parameters.` since the intent is not really to "delete" the variables. – Teebrin May 08 '14 at 18:41
  • 11
    Great, this is the ONLY answer which deals with LEADING unused parameters, as opposed to trailing. The others should be downvoted or deleted ;) – Tomasz Gandor Jan 07 '15 at 10:03
  • 1
    Does the optimizer detect this? – Pepedou Feb 28 '16 at 03:17
  • @boxed Python has an optimizer. I'm not even talking about JIT/hotspot-optimized alternate implementations like PyPy. **CPython** has a bytecode optimizer, it's just very limited because the semantics of the language don't really let it safely make many assumptions when interpreting/compiling from source to bytecode. – mtraceur Jun 07 '19 at 22:27
  • 1
    @Pepedou Probably not in the typical case for now. The `del` itself takes care of "freeing" the argument (so it becomes eligible for garbage collection if nothing else is referencing it). The only other overhead is in the argument passing itself, which should be negligible but cannot be optimized away without whole-program analysis 1) finding and changing every caller to not pass those arguments, 2) ensuring that those optimized callers won't call functions that *do* still use those arguments in this optimized way, and 3) proving that no other caller of the function exists. (cont) – mtraceur Jun 07 '19 at 22:46
  • 1
    @Pepedou (cont) But Python is so dynamic that the third requirement is basically impossible in the general case. A "sufficiently advanced hypothetical optimizer" *could* create code that makes some assumptions, has those optimizations, *and* has checks for those assumptions, *and* has some fallback code for when those fail. That's getting into "hotspot optimization" and "just-in-time compilation" - other Python implementations like PyPy already do some of that. TL;DR: I would not expect unused arguments to be optimized out, but I also think the overhead is utterly negligible. – mtraceur Jun 07 '19 at 23:01
  • Very interesting, thank you. Does this have a runtime cost (for example for small, frequently called, functions)? – Milo Nov 16 '22 at 17:21
  • @Milo yes. But so does passing an argument so that's something to consider.. – boxed Nov 17 '22 at 18:31
84

The underscore is used for things we don't care about and the * in *args denotes a tuple of arguments. Therefore, we can use *_ to denote a tuple of things we don't care about:

def foo(bar, *_):
    return bar

Update with type-hints:

from typing import Any, Tuple

def foo(bar: Any, *_: Tuple[Any, ...]) -> Any:
    return bar
Paul Brown
  • 2,235
  • 12
  • 18
  • 9
    I use `_name` like `_rubbish` for throwaway parameters for three reasons: a) Pycharm doesn't show the warning anymore. b) I remember **what** I throw away. c) There is no conflict with a single underscore. – Thinkeye Jan 04 '18 at 11:44
  • _name is used to denote semi-private variables/class attributes. Using it here doesn't make sense as the objective is to disregard the input. However, it makes so little sense that it's unlikely to cause confusion with the conventional use of _name. – Paul Brown Jan 05 '18 at 13:25
  • 2
    this is a default used by pylint, args matching `ignored-argument-names=_.*` pattern are allowed to be unused – Anentropic May 23 '18 at 11:39
  • The 2 conventions don't collide much: For external names, i.e. class, function, and method names, leading underscore means "don't touch unless you know what you're doing"; for local variables — leading underscore means "unused". However, for arguments, they do collide. Arguments become local variables BUT in Python they *are* part of a function's external interface if caller passes them by name. In that case, you are not at liberty to rename them. Worse, `_arg` convention is *also* used to mean "optional private arg, don't pass unless you know what you're doing". – Beni Cherniavsky-Paskin Aug 14 '19 at 13:40
  • How to define type for `*_`? – Fyzzys Jan 11 '23 at 14:51
  • The args are in a `Tuple` of unknown type (`Any`) and unknown length (`...`), so that'd be: `Tuple[Any, ...]` as in `*_: Tuple[Any, ...]` – Paul Brown Apr 12 '23 at 16:10
65

You can use '_' as prefix, so that pylint will ignore these parameters:

def f(a, _b, _c):
Ronyis
  • 1,803
  • 16
  • 17
39

Here's what I do with unused arguments:

def f(a, *unused):
    return a
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 2
    Hm, nice! Even works with lambdas. However, what to do if I want to ignore the first and third argument but not the second? – Frerich Raabe Apr 05 '12 at 09:16
  • 1
    @FrerichRaabe: then you still have to write out the first. Sorry about that. – Fred Foo Apr 05 '12 at 09:17
  • I see. Pity, in my case it's actually the first (or first two) arguments I want to ignore. Still, your approach is good to know, thanks for sharing! +1 from me. – Frerich Raabe Apr 05 '12 at 09:18
  • @FrerichRaabe: You might want to reconsider your implementation if you are running into this issue. Have you thought about using kwargs? – Joel Cornett Apr 05 '12 at 09:18
  • 2
    @JoelCornett: I'm not sure I understand. Maybe it's my lack of Python expertise - I work with a framework which requires me to pass a callable taking four arguments. The framework eventually calls my function like `f(a,b,c,d)`. It doesn't seem to explicitely name the arguments - would using `kwargs` on my side help in that case? Maybe the framework is suboptimal but what can I say - I have to use what I'm given. :-} – Frerich Raabe Apr 05 '12 at 09:21
  • @FrerichRaabe: If the framework always asks you to pass a callable with 4 arguments, can't you just define a function like `def f(a, b, c, d):` and then just don't use `b` and `d`, or whichever arguments you are trying to ignore? – Joel Cornett Apr 05 '12 at 09:27
  • 1
    @JoelCornett: That's what I do right now. It's just that some arguments have rather long names, so I considered shortening them to something - say, _ - so that I can see that there's a variable being passed - I just happen to not need it (right now). – Frerich Raabe Apr 05 '12 at 09:57
  • @FrerichRaabe: variable names are entirely your decision, so why not `u1`, `u2`, etc. for the unused ones? – Fred Foo Apr 05 '12 at 09:58
  • @FrerichRaabe: a, b, c, d are perfectly good short variable names to use ;) – Joel Cornett Apr 05 '12 at 10:01
  • @larsmans, @JoelCornett: Sure - except that just by looking at the name `u1` or `a` you don't know whether it's unused or not. Hence I was hoping for some sort of convention (much like `_` seems to be a convention when extracting tuples as shown in my question). – Frerich Raabe Apr 05 '12 at 10:17
  • Pylint is not impressed with this 'solution' – Elad Avron Aug 22 '21 at 07:59
  • This has the problem of complaints form linter. `unused` is a name for a variable. The proper way would be to change `unused` to `_`. – Xxxo Dec 16 '21 at 13:20
4

In order to avoid "unused variable" inspection messages for unused *args and/or **kwargs, I replace args and kwargs by _ and __ :

def f(a, b, *_, **__):
    ...

In addition to remove messages, it clearly shows that you don't care about these arguments.

I can't say if it is a really universal solution, but it worked everywhere I've used it until now.

Eric PASCUAL
  • 109
  • 1
  • 8
0

If you have both args and keyword arg you should use

def f(a, *args, **kwargs):
    return a
Maksym Polshcha
  • 18,030
  • 8
  • 52
  • 77
  • I feel like there should be a config setting in pylint for this, it's common to accept catch-all `*args, **kwargs` on functions where you may not use or care about them – Anentropic May 23 '18 at 11:41
  • 1
    Taking all arguments possible in the universe and dropping them on the floor is a terrible suggestion. – boxed May 30 '18 at 12:13
0

I think the accepted answer is bad, but it can still work, if you use what I should call "Perl way" of dealing with arguments (I don't know Perl really, but I quit trying to learn it after seeing the sub syntax, with manual argument unpacking):

Your function has 3 arguments - this is what it gets called with (Duck typing, remember?). So you get:

def funfun(a, b, c):
    return b * 2

2 unused parameters. But now, enter improved larsmans' approach:

def funfun(*args):
    return args[1] * 2

And there go the warnings...

However, I still enjoy more the boxed's way:

def funfun(a, b, c):
    del a, c
    return b * 2

It keeps the self-documenting quality of parameter names. They're a feature, not a bug.

But, the language itself doesn't force you there - you could also go the other way around, and just let all your function have the signature (*args, **kwargs), and do the argument parsing manually every time. Imagine the level of control that gives you. And no more exceptions when being called in a deprecated way after changing your "signature" (argument count and meaning). This is something worth considering ;)

Tomasz Gandor
  • 8,235
  • 2
  • 60
  • 55
  • 2
    I think the fact that you wrote an `# unused` comment is a hint that it's not actually as self-documenting or idiomatic to use `del` like that. – Frerich Raabe Jan 07 '15 at 11:43
  • OK, I removed the comment. Now it is indeed self-documenting. (And seriously: you are arguing that the "ignoring" is not self-documenting. I don't care if the ignore is self-documenting, but if the API is self-documenting). – Tomasz Gandor Jan 08 '15 at 06:59
  • 3
    This does not make the API self-documenting. Furthermore, what's done is done and cannot be undone: the fact that you felt it was necessary to write a comment in the first place, suggests that it's not actually clear what the `del` is doing. Or do you think that obscure code becomes less obscure if you remove comments from it? – Jonathan Cast Oct 17 '17 at 20:00
  • You don't understand what "self documenting API" means in this case: it means the fact that arguments: 1) are present, their number is known; 2) have meaningful names. API means "interface", function signature. Whats below the `def(..)` line - comments or not - is not relevant. – Tomasz Gandor Oct 18 '17 at 08:04
0

You can use "*_" to use multiple unused arguments

def test(x, *_):
    return x
Pankaj Rathi
  • 65
  • 1
  • 11