python中的__all__ 具体用法

一、__all__ 到底是干嘛的?

一句话总结:__all__ 是一个写在 .py 文件里的列表,用来规定:当别人用 from 模块 import * 时,只能导入列表里写的这些内容,其他的都导不进来。

它的核心作用:

  • 控制模块的对外暴露接口,只给用户用你想给的函数 / 类
  • 隐藏内部实现细节,避免用户误调用私有函数
  • 让代码更规范、更易维护(符合工程化设计思想)

二、最基础的用法(直接抄就能用)

1. 先看没有 __all__ 的情况(默认行为)

比如 tool.py

python

复制代码
# tool.py
def add(a, b):
    return a + b

def mul(a, b):
    return a * b

def _internal_calc():  # 内部用的函数,不想给别人用
    return 100

VERSION = "1.0.0"

如果别人这么导入:

python

复制代码
# main.py
from tool import *

print(add(2,3))       # 能拿到
print(mul(4,5))       # 能拿到
print(_internal_calc()) # 也能拿到!(这不是我们想要的)
print(VERSION)        # 也能拿到

默认情况下,import * 会把模块里所有不以下划线开头的名字 全导进来,下划线开头的(比如 _internal_calc)不会导,但其他的全暴露了,很不安全。


2. 加上 __all__ 控制导入

我们在 tool.py 里加一行 __all__

python

复制代码
# tool.py
__all__ = ["add", "mul"]  # 只允许导入这两个函数!

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

def mul(a, b):
    return a * b

def _internal_calc():
    return 100

VERSION = "1.0.0"

再用 from tool import * 试试:

python

复制代码
# main.py
from tool import *

print(add(2,3))       # ✅ 正常运行(在__all__里)
print(mul(4,5))       # ✅ 正常运行(在__all__里)
print(_internal_calc()) # ❌ 报错:NameError: name '_internal_calc' is not defined
print(VERSION)        # ❌ 报错:NameError: name 'VERSION' is not defined

完美!只有 __all__ 列表里的 addmul 能被 import * 导入,其他内容全被挡住了。


三、关键注意点(小白必看,避坑!)

1. __all__ 只对 from 模块 import * 生效!

这是最容易踩的坑:

  • from tool import *:严格遵守 __all__,只导列表里的
  • import tool / from tool import add:完全不受 __all__ 影响!

比如你依然可以这么写:

python

复制代码
# main.py
import tool
print(tool._internal_calc())  # ✅ 能运行!__all__管不到这种导入方式
from tool import _internal_calc # ✅ 也能运行!

__all__ 不是强制的权限控制,只是一个 "约定" 和 "过滤" ,本质是给 import * 做白名单,不是给模块加锁。


2. __all__只能写字符串,对应变量 / 函数 / 类的名字

python

复制代码
# 正确写法
__all__ = ["add", "mul", "User", "CONFIG"]  # 全是字符串,对应模块里的名字

# 错误写法(直接写函数名,会报错)
__all__ = [add, mul]  # ❌ 语法错误,必须是字符串

3. 下划线开头的名字,本来就不会被 import * 导入

哪怕你把 _internal_calc 写进 __all__import * 也不会导它,这是 Python 的默认规则:

python

复制代码
__all__ = ["add", "_internal_calc"]  # 写了也白写,_internal_calc还是导不进来

四、进阶用法:包(文件夹)里统一暴露接口

这正好对应你图里的第二个问题:包(文件夹)里如何统一暴露接口

1. 场景需求

比如你有一个 utils 包,里面有多个 .py 文件:

复制代码
my_project/
├── main.py
└── utils/
    ├── __init__.py
    ├── calc.py
    └── string.py
  • calc.py 里有 addmul
  • string.py 里有 reverse_strupper_str

你希望用户不用写 from utils.calc import add,而是直接 from utils import add,一步到位,这就需要用 __init__.py + __all__ 来统一暴露。

也就是在__init__中统一使用__all__函数来使用


2. 实现步骤

① 先写好子模块

utils/calc.py

python

复制代码
def add(a, b):
    return a + b

def mul(a, b):
    return a * b

utils/string.py

python

复制代码
def reverse_str(s):
    return s[::-1]

def upper_str(s):
    return s.upper()
② 关键:在 __init__.py 里导入 + 写 __all__

utils/__init__.py

python

运行

复制代码
# 从子模块导入需要暴露的函数
from .calc import add, mul
from .string import reverse_str, upper_str

# 用__all__规定:from utils import * 只能导入这些
__all__ = ["add", "mul", "reverse_str", "upper_str"]
③ 主程序直接调用(超级简洁)

main.py

python

复制代码
# 直接从包导入,不用写子模块名!
from utils import add, reverse_str

print(add(2,3))          # ✅ 5
print(reverse_str("abc"))# ✅ cba

# 用*导入也完全正常
from utils import *
print(mul(4,5))          # ✅ 20
print(upper_str("hello"))# ✅ HELLO

五、工程化最佳实践

  1. **每个对外的模块 / 包,都写 __all__**明确对外接口,让用户一眼就知道该用什么,不该用什么。
  2. __all__ 只放公共 API,内部函数一律不写 比如工具类的内部辅助函数、临时变量,都不要出现在 __all__ 里。
  3. 包的 __init__.py 只做接口聚合,不写业务逻辑 __init__.py 就干一件事:把包里的子模块函数导进来,统一暴露,保持干净。
  4. 不要依赖 __all__ 做安全控制 它只是规范,不是权限,真正的私有函数还是要加下划线 _ 来约定。

六、完整可运行示例(跟着敲一遍就会)

项目结构

plaintext

复制代码
demo_all/
├── main.py
└── utils/
    ├── __init__.py
    ├── calc.py
    └── string.py

1. utils/calc.py

python

运行

复制代码
def add(a, b):
    return a + b

def mul(a, b):
    return a * b

def _private_helper():
    return "我是内部函数,别用我"

2. utils/string.py

python

运行

复制代码
def reverse_str(s):
    return s[::-1]

def upper_str(s):
    return s.upper()

3. utils/init.py

python

运行

复制代码
from .calc import add, mul
from .string import reverse_str, upper_str

__all__ = ["add", "mul", "reverse_str", "upper_str"]

4. main.py

python

运行

复制代码
# 方式1:直接从包导入需要的函数
from utils import add, reverse_str
print("add(2,3) =", add(2,3))
print("reverse_str('hello') =", reverse_str("hello"))

# 方式2:用*导入(受__all__控制)
from utils import *
print("mul(4,5) =", mul(4,5))
print("upper_str('world') =", upper_str("world"))

# 尝试导入内部函数(会报错)
# from utils import _private_helper  # 取消注释会报错

运行结果

plaintext

复制代码
add(2,3) = 5
reverse_str('hello') = olleh
mul(4,5) = 20
upper_str('world') = WORLD

七、一句话总结

  • __all__ = ["函数名1", "函数名2"]:给 from 模块 import * 做白名单,只导列表里的
  • 只对 import * 生效,import 模块 不受影响
  • 包的 __init__.py__all__ 统一暴露接口,让用户调用更简单

你现在对 __all__ 的用法完全理解了吗?要不要我再给你补一个类的 __all__ 用法 ,或者结合你之前的工程化会话项目,给你加一套完整的 __all__ 接口设计?

相关推荐
Pkmer几秒前
Java程序员大战Python面向对象
python·ai编程
小龙报7 分钟前
【Coze-AI智能体平台】低代码省时高效:Coze 应用开发全流程指南
java·人工智能·python·深度学习·低代码·chatgpt·交互
技术钱8 分钟前
LCEL表达式与Runnable可运行协议
python
Echo_NGC223711 分钟前
【论文解读】Attention Is All You Need —— AI 时代的“开山之作“,经典中的经典(transformer小白导读)
人工智能·python·深度学习·神经网络·机器学习·conda·transformer
陈随易12 分钟前
bun将会支持Bun.image,你怎么看?
前端·后端·程序员
勿忘初心122123 分钟前
【Java实战】SpringBoot 集成 freemarker 导出 Word 模板
java·spring boot·freemarker·模板引擎·word导出·后端实战
jingqingdai329 分钟前
别用正则格式化 HTML!我用 DOM 遍历实现零风险本地格式化,老项目重构效率直接拉满
前端·重构·html
绿草在线29 分钟前
SpringBoot项目实战:从零搭建高效开发环境
java·spring boot·后端
J2虾虾31 分钟前
Java Lambda 表达式详解文档
java·开发语言