Python Module Development Guide

Have you ever had this experience: you write a practical crawler or tool script, add more and more functions, and then open it again a few months later - hundreds of lines of code are mixed together, you have to look up and down to find a function, and you have to copy, paste and modify it for a long time when you change the project to reuse it?

From "running" to "maintainable and reusable", the first step is to learn to write standardized Python modules**. Today's article starts from the most basic templates, to common import postures, to encapsulation and best practices, to help you write clear, professional code that others are willing to use.


Module Basics

Let’s review the core concepts first (don’t look at it as simple, many people are still stuck after using it for a year or two): **Python module is essentially a module based on.pyThe text file at the end is **, and the file name is the natural module name. It can contain functions, classes, global variables, and even execution logic - but standard modules will only put one-time initialization or test code at the end.

A ready-to-use module template

The following template is compatible with Python 3.6+ and follows the PEP 8/257/484 specification. Just copy, rename, and fill in your own content:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""用户友好型问候模块

提供公开的 `greet()` 和 `farewell()` 函数,支持自定义问候语气。
也可以通过命令行直接调用:
- 无参数:输出通用问候
- 1个参数:输出指定人名的问候
"""

__author__ = "CodeDog2024"
__version__ = "0.1.0"
__license__ = "MIT"

# 导入区(模块文档后、全局变量前)
import sys
from typing import Optional

# 常量区(全大写、下划线分隔,尽量放在导入后)
_DEFAULT_GREETING = "Hello"
_DEFAULT_FAREWELL = "Goodbye"

# 私有辅助函数/类(单下划线开头,仅内部用)
def _build_message(template: str, name: Optional[str] = None) -> str:
    """内部辅助:根据模板和人名生成完整消息"""
    if not name:
        return f"{template}!"
    return f"{template}, {name}!"

# 公开函数/类(普通命名,放在辅助之后)
def greet(name: Optional[str] = None, custom_template: Optional[str] = None) -> str:
    """生成正式/自定义的问候语
    
    Args:
        name: 要问候的对象名称,省略则输出通用问候
        custom_template: 自定义问候模板,省略用默认 "Hello"
        
    Returns:
        格式化后的问候字符串
    """
    template = custom_template or _DEFAULT_GREETING
    return _build_message(template, name)

def farewell(name: Optional[str] = None, custom_template: Optional[str] = None) -> str:
    """生成正式/自定义的告别语
    
    Args:
        name: 要告别的对象名称,省略则输出通用告别
        custom_template: 自定义告别模板,省略用默认 "Goodbye"
        
    Returns:
        格式化后的告别字符串
    """
    template = custom_template or _DEFAULT_FAREWELL
    return _build_message(template, name)

# 主入口区(仅当模块被直接执行时运行)
def main(args: list[str]) -> None:
    """处理命令行参数的主入口"""
    name = args[1] if len(args) > 1 else None
    print(greet(name))

if __name__ == "__main__":
    main(sys.argv)

Why do you write it like this? Disassembled piece by piece

Many novices will just paste the template, but fail to notice that these "inconspicuous" details are the basis of a good module.

  1. Top Two Rows (optional but highly recommended)

    • #!/usr/bin/env python3: Under Unix/Linux/macOS, add this line and thenchmod +x, you can execute the script just like./hello.pyRun directly.
    • # -*- coding: utf-8 -*-: Although Python 3 defaults to UTF-8, retaining this line can make it compatible with some old tools or document generators. It is more secure to write an extra line.
  2. Module-level documentation string (Docstring, must be written!)

  • The first paragraph of the module must be wrapped in triple quotes and briefly describe what the module does, what core functions it has, and whether there are any special command line usages.
  • This information can be obtained through模块名.__doc__orhelp(模块名)Viewing is the first impression you make on others (including yourself three months later).
  1. Meta information variables (Public special variables)
    • __author____version____license__These variables allow users to know at a glance who wrote it, which version, and whether it can be used commercially.
  • The version number is recommended to follow Semantic Version (主版本.次版本.补丁版本): Upgrade the main version for incompatible changes of major functions, upgrade the minor version for new backward compatibility functions, and upgrade the patch version purely for bug fixes.
  1. Import order (strictly according to PEP 8)
  • Write the standard library first (such assystyping
  • Then write third-party library (such asrequestspandas
  • Finally write local module
  • Leave a blank line between each category, and sort them in alphabetical order (use the IDE's automatic sorting plug-in to do it with one click, obsessive-compulsive disorder is ecstatic).
  1. Constant → private auxiliary → public interface order
  • Put global constants first (all caps + underline, so you can locate adjustable parameters at a glance)
  • Followed by functions or classes that are only used internally (single underscore_At the beginning, it is equivalent to "don't blame me if you touch it")
  • Finally, there are public functions/classes provided to the outside world, so that when readers look from top to bottom, the logic goes from support to application, making reading smoother.
  1. if __name__ == "__main__":Main entrance (must master)
  • When the module is run directly,__name__will be automatically set by Python to"__main__", thus triggering the following code.
  • When the module is imported by other files,__name__is the module name (such asgreet_tool), the main code will not be executed.
  • This is usually used to write quick tests or command line call logic.

Two ways to use modules

Method 1: Run as a standalone script

Save the above template asgreet_tool.py, you can experience it by typing the command directly:

# 普通执行
$ python3 greet_tool.py
Hello!

# 传一个名字
$ python3 greet_tool.py Python
Hello, Python!

# Unix/Linux/macOS 下加执行权限后可以直接 ./ 运行
$ chmod +x greet_tool.py
$ ./greet_tool.py Alice
Hello, Alice!

Method 2: Import as a reusable component

This is the daily way to open a module - to be imported by others and provide services quietly.

Basic import (full quote)

import greet_tool

# 用模块名调用公开函数
print(greet_tool.greet("Bob"))                     # Hello, Bob!
print(greet_tool.farewell("Bob", "See you"))       # See you, Bob!

# 查看元信息
print(f"Version: {greet_tool.__version__}")        # Version: 0.1.0

Partial import (only take what is needed)

from greet_tool import greet

print(greet("Charlie"))   # Hello, Charlie!
# farewell 用不了,因为没导入

Alias ​​import (resolve name conflicts or simplify long names)

import greet_tool as gt
print(gt.greet("Dave"))

from greet_tool import farewell as bye
print(bye("Eve"))

Python’s “pseudo-private” scope control

Python does not have the samepublic/private/protectedKeywords rely entirely on naming convention to achieve "soft restrictions".

Named typeExampleDescription
Public membersgreet, PIImport and use at will, it is the external interface of the module
Inside Tips Member_build_messageStarting with a single underscore, just reminds "it's best not to use it", but it is mandatoryimport _build_messageStill accessible (gentleman's agreement)
Name modified members__secret_dataBegins with a double underscore, triggering Python's name modification (becomes_类名__secret_data), which is more difficult to access directly, but it is not absolutely locked (beware of gentlemen but not villains)

Do a little experiment on name modification: ingreet_tool.pyAdd a double underscore variable here:

__top_secret_key = "123456"

Then try it in an interactive environment:

import greet_tool

print(greet_tool.__top_secret_key)  # AttributeError: module 没有这个属性
# 但还是能通过修饰后的名字拿到
print(greet_tool._greet_tool__top_secret_key)  # 123456

In general development, a single underscore is sufficient to indicate internal use. Double underscores are mainly used in object-oriented situations when it is necessary to prevent subclasses from unintentionally overriding methods with the same name of the parent class. It will not be too late to advance further in the object-oriented field in the future.


5 best practices for writing good modules quickly

1. Single responsibility, a module only does one thing

  • data_parser.pySpecialize in parsing data, don’t stuff crawler logic into it
  • logger_util.pyManage logging, do not configure database connections at the same time

If the module exceeds 500~800 lines, or there are multiple unrelated functions, you should consider splitting it into packages (this topic will not be expanded on today).

2. Document string + type annotation, two-pronged approach

  • Documentation string: Show it to others and explain clearly "how to use it, what the parameters are, and what is returned". Google style or NumPy style is recommended, which is simple and intuitive.
  • Type annotations: for editors and static checking tools (mypyetc.) See, auto-completion is smarter when writing code, and it can also catch type errors before running.

3. Make good use ofmainDo a quick test on the entrance

After writing the public function, convenientlyif __name__ == "__main__":Riga asserts a few lines:

if __name__ == "__main__":
    assert greet() == "Hello!"
    assert greet("Frank") == "Hello, Frank!"
    assert greet("Frank", "Hi") == "Hi, Frank!"
    print("All quick tests passed!")

In this way, every time you change the code and run the module directly, you can immediately check whether the basic functions are damaged (it is still recommended for formal testing)pytestorunittest)。

4. Optional dependenciestry-exceptpack

If a certain function of the module requires a third-party library (for example, when drawing pictures,matplotlib), Don’t import directly at the top, otherwise people who haven’t installed this library won’t even be able to use the basic functions:

# ❌ 错误写法
import matplotlib.pyplot as plt

# ✅ 正确写法
_MATPLOTLIB_AVAILABLE = False
try:
    import matplotlib.pyplot as plt
    _MATPLOTLIB_AVAILABLE = True
except ImportError:
    pass

def plot_something():
    if not _MATPLOTLIB_AVAILABLE:
        raise ImportError("请先安装 matplotlib: pip install matplotlib")
    # 绘图逻辑……

5. Follow PEP 8 coding style

  • Use 4 spaces for indentation (don’t use Tab)
  • Try not to exceed 79/120 characters per line
  • Add spaces before and after the operator (a = b + c, instead ofa=b+c
  • Two empty lines between functions/classes, and one empty line between internal logic blocks of methods

Modern editors (VS Code, PyCharm, etc.) can be installedblackautopep8This type of plug-in enables one-click formatting, which is worry-free and standard.


Good modules are the cornerstone of reusable code. Today's content covers the entire process from template construction to best practices, which is enough for you to write professional and clear modules in most daily development scenarios. Next time you write a script, remember to take out this template to make the code clean from the beginning~