Python: avoiding Pylint warnings about too many arguments

PythonRefactoringPylint

Python Problem Overview


I want to refactor a big Python function into smaller ones. For example, consider this following code snippet:

x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9

Of course, this is a trivial example. In practice, the code is more complex. My point is that it contains many local-scope variables that would have to be passed to the extracted function, which could look like:

def mysum(x1, x2, x3, x4, x5, x6, x7, x8, x9):
    x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
    return x

The problem is that Pylint would trigger a warning about too many arguments.

I could avoid the warning by doing something like:

def mysum(d):
    x1 = d['x1']
    x2 = d['x2']
    ...
    x9 = d['x9']
    x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
    return x

def mybigfunction():
    ...
    d = {}
    d['x1'] = x1
    ...
    d['x9'] = x9
    x = mysum(d)

but this approach loos ugly to me. It requires writing a lot of code that is even redundant.

Is there a better way to do it?

Python Solutions


Solution 1 - Python

First, one of Perlis's epigrams:

> "If you have a procedure with 10 > parameters, you probably missed some."

Some of the 10 arguments are presumably related. Group them into an object, and pass that instead.

Making an example up, because there's not enough information in the question to answer directly:

class PersonInfo(object):
  def __init__(self, name, age, iq):
    self.name = name
    self.age = age
    self.iq = iq

Then your 10 argument function:

def f(x1, x2, name, x3, iq, x4, age, x5, x6, x7):
  ...

becomes:

def f(personinfo, x1, x2, x3, x4, x5, x6, x7):
  ...

and the caller changes to:

personinfo = PersonInfo(name, age, iq)
result = f(personinfo, x1, x2, x3, x4, x5, x6, x7)

Solution 2 - Python

Do you want a better way to pass the arguments or just a way to stop Pylint from giving you a hard time? If the latter, you can stop the nagging by putting Pylint-controlling comments in your code along the lines of:

#pylint: disable=R0913

or, better:

#pylint: disable=too-many-arguments

remembering to turn them back on as soon as practicable.

In my opinion, there's nothing inherently wrong with passing a lot of arguments and solutions advocating wrapping them all up in some container argument don't really solve any problems, other than stopping Pylint from nagging you :-).

If you need to pass twenty arguments, then pass them. It may be that this is required because your function is doing too much and refactoring could assist there, and that's something you should look at. But it's not a decision we can really make unless we see what the 'real' code is.

Solution 3 - Python

You can easily change the maximum allowed number of arguments in Pylint. Just open your pylintrc file (generate it if you don't already have one) and change:

max-args = 5

to:

max-args = 6 # Or any value that suits you

From Pylint's manual

> Specifying all the options suitable > for your setup and coding standards > can be tedious, so it is possible to > use a rc file to specify the default > values. Pylint looks for /etc/pylintrc > and ~/.pylintrc. The --generate-rcfile > option will generate a commented > configuration file according to the > current configuration on standard > output and exit. You can put other > options before this one to use them in > the configuration, or start with the > default values and hand tune the > configuration.

Solution 4 - Python

You could try using Python's variable arguments feature:

def myfunction(*args):
    for x in args:
        # Do stuff with specific argument here

Solution 5 - Python

Perhaps you could turn some of the arguments into member variables. If you need that much state a class sounds like a good idea to me.

Solution 6 - Python

Simplify or break apart the function so that it doesn't require nine arguments (or ignore Pylint, but dodges like the ones you're proposing defeat the purpose of a lint tool).

If it's a temporary measure, disable the warning for the particular function in question using a comment as described in Pylint: Disable-msg for a block or statement?

Later, you can grep for all of the disabled warnings.

Solution 7 - Python

I do not like referring to the number. The symbolic name is much more expressive and avoid having to add a comment that could become obsolete over time.

So I'd rather do:

#pylint: disable-msg=too-many-arguments

And I would also recommend to not leave it dangling there: it will stay active until the file ends or it is disabled, whichever comes first.

So better do:

#pylint: disable-msg=too-many-arguments
code_which_would_trigger_the_msg
#pylint: enable-msg=too-many-arguments

I would also recommend enabling/disabling one single warning/error per line.

Solution 8 - Python

Python has some nice functional programming tools that are likely to fit your needs well. Check out lambda functions and map. Also, you're using dicts when it seems like you'd be much better served with lists. For the simple example you provided, try this idiom. Note that map would be better and faster but may not fit your needs:

def mysum(d):
   s = 0  
   for x in d:
        s += x
   return s

def mybigfunction():
   d = (x1, x2, x3, x4, x5, x6, x7, x8, x9)
   return mysum(d)

You mentioned having a lot of local variables, but frankly if you're dealing with lists (or tuples), you should use lists and factor out all those local variables in the long run.

Solution 9 - Python

Use a dataclass:

from dataclasses import dataclass

# declare your data structure
@dataclass
class Struct:
    x1: int
    x2: int
    x3: int
    x4: int
    x5: int

# declare your function operating on this structure
def mysum(s: Struct):
    return s.x1 + s.x2 + s.x3 # ...

def mybigfunction():
    s = Struct(1,2,3,4,5) # instantiate your structure
    x = mysum(s)
    return x

Then mybigfunction() returns 6 (1+2+3).

This is a nice way to group, organize, and document your arguments. It also simplifies type hinting of functions.

Note that all the dataclass decorator does is making your work easier by implementing the __init__ method and others.

Solution 10 - Python

For Python3 you should just use keyword-only arguments:

File pylint_args_too_many.py
"""Example of a function causing pylint too-many-arguments"""

def get_car(
    manufacturer, model, year=None, registration_number=None, vin=None, color=None
):
    """Returns dict with all required car attributes"""
    return dict(
        manufacturer=manufacturer,
        model=model,
        year=year,
        registration_number=registration_number,
        vin=vin,
        color=color,
    )

print(repr(get_car(manufacturer="ACME", model="Rocket")))
pylint pylint_args_too_many.py 

************* Module pylint_args_too_many
pylint_args_too_many.py:3:0: R0913: Too many arguments (6/5) (too-many-arguments)

------------------------------------------------------------------
Your code has been rated at 6.67/10 (previous run: 6.67/10, +0.00)
File pylint_args.py
"""Show how to avoid too-many-arguments"""

def get_car(
    *, manufacturer, model, year=None, registration_number=None, vin=None, color=None
):
    """Returns dict with all required car attributes"""
    return dict(
        manufacturer=manufacturer,
        model=model,
        year=year,
        registration_number=registration_number,
        vin=vin,
        color=color,
    )

print(repr(get_car(manufacturer="ACME", model="Rocket")))
pylint pylint_args.py 

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

Solution 11 - Python

I came across the same nagging error, which I realized had something to do with a cool feature PyCharm automatically detects...just add the @staticmethod decorator, and it will automatically remove that error where the method is used.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnonymousView Question on Stackoverflow
Solution 1 - Pythonuser97370View Answer on Stackoverflow
Solution 2 - PythonpaxdiabloView Answer on Stackoverflow
Solution 3 - PythonNadia AlramliView Answer on Stackoverflow
Solution 4 - PythonhbwView Answer on Stackoverflow
Solution 5 - PythonBrian RasmussenView Answer on Stackoverflow
Solution 6 - PythonDaveView Answer on Stackoverflow
Solution 7 - PythonIgor StoppaView Answer on Stackoverflow
Solution 8 - PythoneaselView Answer on Stackoverflow
Solution 9 - PythonHugo TrentesauxView Answer on Stackoverflow
Solution 10 - PythonTometzkyView Answer on Stackoverflow
Solution 11 - PythonAntsView Answer on Stackoverflow