Python **無固定單一入口文件**,入口由**執行方式決定**:
python main.py → main.py 是入口。python -m mypackage → mypackage/__main__.py 是入口。pip install . 生成 console_scripts → setup.py/pyproject.toml 指定的函數是入口。
myproject/
├── pyproject.toml # 現代構建配置 (PEP 621)
├── README.md
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── __main__.py # `python -m mypackage` 入口
│ ├── cli.py # 命令行解析 (argparse/click/typer)
│ ├── core.py # 核心業務邏輯
│ └── utils.py
├── tests/
│ └── test_core.py
└── scripts/ # 可選:部署/運維腳本
└── deploy.sh
src/mypackage/__main__.py# 當執行 `python -m mypackage` 時運行
from .cli import main
if __name__ == "__main__":
main()
src/mypackage/cli.pyimport argparse
from .core import run_task
def main():
parser = argparse.ArgumentParser(description="My Awesome Project")
parser.add_argument("input", help="輸入文件路徑")
parser.add_argument("-o", "--output", help="輸出目錄")
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()
run_task(args.input, args.output, args.verbose)
if __name__ == "__main__":
main()
pyproject.toml (現代標準)[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "myproject"
version = "0.1.0"
description = "Demo project structure"
authors = [{name = "You", email = "you@example.com"}]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.8"
dependencies = ["click", "requests"]
[project.optional-dependencies]
dev = ["pytest", "black", "ruff"]
[project.scripts]
mycli = "mypackage.cli:main" # 安裝後生成 `mycli` 命令
[tool.setuptools.packages.find]
where = ["src"]
| 方式 | 命令 | 入口文件 | 適用場景 |
|---|---|---|---|
| 直接運行腳本 | python src/mypackage/cli.py args |
指定的 .py 文件 | 開發調試、臨時腳本 |
| 模塊模式 | python -m mypackage args |
__main__.py |
已安裝包、生產環境、保持導入路徑正確 |
| 安裝後命令 | pip install -e . && mycli args |
console_scripts 指向函數 |
最終用戶分發、CI/CD、系統服務 |
main.py 並 python main.pysys.path,易出錯。__main__.py 卻用 python -m mypackageNo module named __main__。src/ 佈局 (src layout)