python
private functions
module definition
programming
software development

Defining private module functions in python

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python does not have true private module functions enforced by the language. Instead, module privacy is mostly a naming convention: prefix internal functions with a single underscore to signal that they are for internal use and should not be treated as part of the public API.

The Standard Convention: Leading Underscore

A module-level helper function intended for internal use is usually written like this:

python
1def _normalize_name(text: str) -> str:
2    return " ".join(text.strip().lower().split())
3
4
5def display_name(text: str) -> str:
6    normalized = _normalize_name(text)
7    return normalized.title()

Other developers can still import _normalize_name, but the underscore communicates that doing so is discouraged. In Python, that social contract is the normal approach to module privacy.

What the Convention Actually Affects

The underscore does not block access:

python
from names import _normalize_name

print(_normalize_name("  ALICE   SMITH "))

That works. The function is not hidden. But tools and conventions often treat underscore-prefixed names as non-public.

For example, from module import * skips names that start with a single underscore unless the module explicitly exports them.

Use __all__ to Define the Public Surface

If you want to be explicit about what a module exports, define __all__:

python
1__all__ = ["display_name"]
2
3
4def _normalize_name(text: str) -> str:
5    return " ".join(text.strip().lower().split())
6
7
8def display_name(text: str) -> str:
9    return _normalize_name(text).title()

Now wildcard imports expose only display_name. This does not make _normalize_name private in an absolute sense, but it does document the intended public API more clearly.

Double Underscores Do Not Mean Module Privacy

Some developers try module-level names such as __helper and expect name mangling. That is a class feature, not a module feature.

At module scope, double underscores mostly create an odd-looking name. They do not give you stronger privacy. For modules, the normal choice is still a single leading underscore.

Why Python Chooses Conventions

Python favors readability and adult-level responsibility over strict encapsulation for modules. The idea is:

  • public names are part of the supported interface
  • underscore names are implementation details

That keeps modules simple and avoids heavy access-control machinery where it is usually unnecessary.

A Good Practical Pattern

Many modules use a layered structure:

  • public functions without underscores
  • internal helpers with a single underscore
  • '__all__ to document the intended exports'

That gives users a clean API while letting the module keep its internal structure readable.

Common Pitfalls

The biggest pitfall is assuming underscore-prefixed functions are actually inaccessible. They are not. The underscore is a convention, not an enforcement mechanism.

Another common mistake is using double underscores at module level and expecting class-style name mangling. Python does not apply that rule to module functions.

People also forget to think about API stability. If outside code starts depending on underscore-prefixed helpers, later refactors become harder because those internal details have leaked into the public surface.

Summary

  • Python does not provide truly private module functions enforced by the language.
  • Use a single leading underscore to mark internal helper functions.
  • Define __all__ if you want to make the intended public API explicit.
  • Do not expect double underscores at module scope to provide stronger privacy.
  • Treat underscore-prefixed names as implementation details that may change without notice.
  • Use conventions consistently so other developers can tell at a glance which module helpers are public and which are internal.
  • Prefer a clear public API over trying to hide implementation details with naming tricks.
  • Internal helpers should remain easy to refactor.

Course illustration
Course illustration

All Rights Reserved.