Fundamentals 13 min read

How to Write Good Python Functions: Six Guidelines for Idiomatic Code

This article outlines six practical guidelines for writing clean, maintainable Python functions—including meaningful naming, single responsibility, comprehensive docstrings, returning useful values, limiting length, and ensuring idempotence or purity—illustrated with code examples and explanations to help developers improve function quality.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
How to Write Good Python Functions: Six Guidelines for Idiomatic Code

Python is powerful but writing good functions can be challenging. This article, based on the Medium post "Writing Idiomatic Python", presents six recommendations to help developers create clean, readable, and testable functions.

What Makes a Good Function?

A function is considered good when it satisfies most of the following criteria:

Reasonable naming

Single responsibility

Includes a docstring

Returns a value

Does not exceed 50 lines

Is idempotent or pure

Naming

Good names should avoid obscure abbreviations and convey the function’s purpose. For example, the poorly named function: def get_knn(from_df): uses the abbreviation knn (K‑Nearest Neighbors) and the vague verb get. A clearer alternative is: def k_nearest_neighbors(dataframe): This name tells a reader exactly what the function does and what type of argument it expects.

Single Responsibility

A function should do only one thing. Combining calculation and printing, as in the following example, violates this principle:

def calculate_and_print_stats(list_of_numbers):
    sum = sum(list_of_numbers)
    mean = statistics.mean(list_of_numbers)
    median = statistics.median(list_of_numbers)
    mode = statistics.mode(list_of_numbers)
    print('---Stats---')
    print('SUM: {}'.format(sum))
    print('MEAN: {}'.format(mean))
    print('MEDIAN: {}'.format(median))
    print('MODE: {}'.format(mode))

It is better to split the logic into two functions: one that computes and returns the statistics, and another that formats and prints them.

Docstrings

Every function should have a docstring that follows PEP‑257: a concise one‑sentence summary, proper punctuation, and clear description of parameters and return values. Writing the docstring before the implementation encourages thoughtful design.

Return Values

Functions should always return a useful value. Even if the primary purpose is I/O, returning a status (e.g., True) makes testing easier. When multiple values are needed, return a tuple.

Example of a function without an explicit return (implicitly returns None):

def add(a, b):
    print(a + b)

Contrast with a proper implementation:

def add(a, b):
    return a + b

Function Length

Long functions are hard to understand and maintain. Keeping functions under about 50 lines (often much shorter) improves readability. If a function grows too large, refactor by extracting logical blocks into smaller helper functions.

Idempotence and Purity

An idempotent function returns the same result for the same inputs, regardless of how many times it is called. Example of an idempotent function:

def add_three(number):
    """Return number + 3."""
    return number + 3

A non‑idempotent version that reads user input each call:

def add_three():
    """Return 3 + the number entered by the user."""
    number = int(input('Enter a number: '))
    return number + 3

Pure functions are a stricter subset: they are idempotent and have no observable side effects (no I/O, no modification of external state). Pure functions are easier to test and reason about.

Why Idempotence and Purity Matter

Both properties simplify testing, refactoring, and reasoning about code. Idempotent functions can be called repeatedly without changing program state, while pure functions guarantee that the only effect of a call is its return value.

In practice, aiming for pure or at least idempotent functions leads to more maintainable and reliable code, even though it may not always be possible to eliminate all side effects.

Overall, being conscious about naming, responsibility, documentation, return values, length, and functional purity helps developers write better Python functions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonSoftware Engineeringcodingidiomatic
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.