127

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?

13
  • 4
    Why not just have default values for your arguments? Why would you have unused arguments in a function? Commented Apr 5, 2012 at 9:05
  • 23
    @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. Commented Apr 5, 2012 at 9: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. Commented Apr 5, 2012 at 9:30
  • 2
    @jamylak: Having default values for the arguments doesn't make them used, so you would still have the problem. Commented Sep 27, 2016 at 12:50
  • 6
    @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. Commented Sep 28, 2016 at 6:53

8 Answers 8

165

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.
Sign up to request clarification or add additional context in comments.

8 Comments

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.
Great, this is the ONLY answer which deals with LEADING unused parameters, as opposed to trailing. The others should be downvoted or deleted ;)
Does the optimizer detect this?
@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)
@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.
|
100

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

9 Comments

That's in Python 3.
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.
_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.
this is a default used by pylint, args matching ignored-argument-names=_.* pattern are allowed to be unused
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".
|
78

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

def f(a, _b, _c):

1 Comment

This silences the ruff "ARG001 Unused function argument" nicely.
39

Here's what I do with unused arguments:

def f(a, *unused):
    return a

12 Comments

Hm, nice! Even works with lambdas. However, what to do if I want to ignore the first and third argument but not the second?
@FrerichRaabe: then you still have to write out the first. Sorry about that.
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.
@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. :-}
@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).
|
5

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.

Comments

1

If you have both args and keyword arg you should use

def f(a, *args, **kwargs):
    return a

2 Comments

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
Taking all arguments possible in the universe and dropping them on the floor is a terrible suggestion.
1

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

def test(x, *_):
    return x

Comments

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 ;)

4 Comments

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.
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).
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?
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.