too-many-positional-arguments / R0917¶
Message emitted:
Too many positional arguments (%s/%s)
Description:
Used when a function has too many positional arguments.
Problematic code:
# +1: [too-many-positional-arguments]
def calculate_drag_force(velocity, area, density, drag_coefficient):
"""Each argument is positional-or-keyword unless a `/` or `*` is present."""
return 0.5 * drag_coefficient * density * area * velocity**2
drag_force = calculate_drag_force(30, 2.5, 1.225, 0.47)
Correct code:
def calculate_drag_force(*, velocity, area, density, drag_coefficient):
"""This function is 'Keyword only' for all args due to the '*'."""
return 0.5 * drag_coefficient * density * area * velocity**2
# This is now impossible to do and will raise a TypeError:
# drag_force = calculate_drag_force(30, 2.5, 1.225, 0.47)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# TypeError: calculate_drag_force() takes 0 positional arguments but 4 were given
# And this is the only way to call 'calculate_drag_force'
drag_force = calculate_drag_force(
velocity=30, area=2.5, density=1.225, drag_coefficient=0.47
)
Configuration file:
[DESIGN]
max-positional-arguments=3
Additional details:
Good function signatures don’t have many positional parameters. For almost all interfaces, comprehensibility suffers beyond a handful of arguments.
Positional arguments work well for cases where the the use cases are
self-evident, such as unittest's assertEqual(first, second, "assert msg")
or zip(fruits, vegetables)
.
There are a few exceptions where four or more positional parameters make sense,
for example rgba(1.0, 0.5, 0.3, 1.0)
, because it uses a very well-known and
well-established convention, and using keywords all the time would be a waste
of time.
Related links:
Created by the design checker.