如何利用__init__.py构建简洁API并解决暴露相关问题
问题1:防止依赖库通过API暴露的最佳实践
有几种可靠的方式解决这个问题:
只导出需要的接口,不暴露内部模块:不要在
core/__init__.py里导入整个calculator或exceptions模块,而是直接从这些模块导入你想对外提供的函数、类或异常。示例:# core/__init__.py from .calculator import add_numbers, multiply_numbers # 仅导入需要的函数 from .exceptions import CalculationError, InvalidInputError # 仅导入需要的异常这种方式下,用户无法通过
API.core.calculator访问到内部模块,自然也看不到模块里导入的numpy、pandas等依赖。用
__all__明确限定对外导出内容:如果习惯用from .calculator import *的写法,在calculator.py里定义__all__变量,只列出你想对外暴露的对象,这样通配符导入只会导入指定内容,不会带入库依赖:# calculator.py import numpy as np __all__ = ['add_numbers', 'multiply_numbers'] def add_numbers(a, b): return np.add(a, b)同时在
core/__init__.py里使用from .calculator import *,但因为calculator.py的__all__限制,只会导入指定函数,且用户看不到calculator模块本身(只要不在core/__init__.py里导入calculator模块)。将依赖设为模块私有(辅助手段):在模块内部导入依赖时,给依赖名加上下划线前缀,比如
import numpy as _np。按照Python惯例,下划线开头的名称是内部私有元素,用户不应访问。不过这只是约定而非强制限制,建议结合前两种方法使用。
问题2:避免函数同时通过包名和模块名访问
核心思路是不要把内部模块(如calculator、exceptions)导入到父包的命名空间中,只把需要的函数/类直接导入到父包层级:
- 在
core/__init__.py里,仅导入具体的函数和异常,不导入内部模块:# core/__init__.py from .calculator import my_function from .exceptions import CalculationFailed - 顶层
API/__init__.py里只导入core:# API/__init__.py from . import core
这样用户只能通过API.core.my_function()调用函数,API.core.calculator这个路径根本不存在,不会出现重复访问的情况。
如果需要保留内部模块结构但不想让用户访问,也可以导入模块后用del语句移除引用:
# core/__init__.py from . import calculator from .calculator import my_function del calculator # 移除模块引用,用户无法访问API.core.calculator
不过这种方式不如直接导入目标对象简洁,更推荐第一种方案。
内容的提问来源于stack exchange,提问作者Loris Constantin

