How many arguments should a function have in Python?

One of the clearest signs that a function is doing too much is that it has too many arguments. When there are many arguments, the code becomes hard to read, test, and document. What is the limit?

A simple rule that works well in practice:

  • Ideal: 0 to 2 arguments.
  • Acceptable: 3 or 4 (if well justified or using default values).
  • Bad design: 5 to infinity.

But there are functions that need more arguments — for example, when creating a user, you need first name, last name, email, phone, address... There must be a solution that does not break the 4-argument rule.

Here are some tools to consider. You decide which one fits your case best.

Dataclasses

If you have several related arguments, group them into a dataclass. The function receives a single object with everything it needs.

from dataclasses import dataclass


@dataclass
class Address:
    """Full postal address of a user.

    Attributes:
        street: Street name and number.
        city: City of residence.
        zip_code: Postal code of the area.
    """

    street: str
    city: str
    zip_code: str


def create_user(
    first_name: str, last_name: str, email: str, phone: str, address: Address
) -> None:
    """Create a new user in the system.

    Args:
        first_name: User's first name.
        last_name: Last name.
        email: Email address.
        phone: Contact phone number.
        address: Full postal address.
    """
    # Your logic here

Keyword-only arguments

The * in the signature forces everything that comes after it to be passed by name. Its main advantage is that it improves readability, since the code that calls the function is more explicit.

def configure_chart(
    title: str,
    *,
    width: int = 800,
    height: int = 600,
    line_color: str = "blue",
    thickness: int = 2,
) -> None:
    """Configure and render a chart.

    Args:
        title: Chart title.
        width: Width in pixels, default 800.
        height: Height in pixels, default 600.
        line_color: Main line color, default "blue".
        thickness: Line thickness in points, default 2.
    """
    pass


configure_chart("My Chart", width=1024, line_color="red")

Validation with msgspec

Work with a data validation library, for example msgspec. If you have used pydantic, you will feel right at home. Think of msgspec as a modern validator and parser, lightweight (written in C) and very efficient.

import msgspec


class User(msgspec.Struct, strict=True):
    """User model with strict type validation.

    Attributes:
        name: Full name of the user.
        age: Age in years.
    """

    name: str
    age: int


input_data = {"name": "Ana", "age": 28}

validated_user = msgspec.convert(input_data, type=User)

With strict=True, if a type does not match exactly, it raises an error. No surprises.

Final notes

I have not included *args and **kwargs because they only hide the arguments. They are useful in specific contexts (decorators, wrappers, very generic APIs), but they are not an excuse to avoid thinking about design.

Hopefully, before adding a fifth argument, you ask yourself whether it really belongs there or whether it is a sign that the function is taking on too many responsibilities.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Will you buy me a coffee?

Comments

There are no comments yet.

Written by Andros Fenollosa

May 27, 2026

2 min of reading

You may also like

Visitors in real time

You are alone: 🐱