builtins
模块是 Python 中非常核心且强大的一个模块,它包含了所有 内置函数、异常和对象(如 print()
, len()
, int
, str
, Exception
等)。你可以通过修改或替换这些内置对象来实现“魔改”运行时行为的目的。
🔍 一、什么是 builtins
?
在 Python 启动后,所有的内置变量和函数都会被加载到 builtins
模块中。每个模块的全局命名空间中都有一个名为 __builtins__
的引用,指向这个模块。
import builtins
print(builtins.print) # <built-in function print>
🛠️ 二、如何“魔改” builtins?
在运行时动态地修改、替换甚至删除内置函数或对象,从而改变程序的行为。
✅ 示例 1:重写 print()
函数
import builtins
original_print = builtins.print
def my_print(*args, **kwargs):
original_print("[LOG]", *args, **kwargs)
builtins.print = my_print
print("Hello World") # 输出: [LOG] Hello World
这个技巧可以用于调试、日志记录、安全沙箱等场景。
✅ 示例 2:禁用某些内置函数
比如你想禁止用户调用 eval()
或 exec()
:
import builtins
# 方法一:直接删除
del builtins.eval
del builtins.exec
# 方法二:替换为抛出异常的函数
def restricted(*args, **kwargs):
raise RuntimeError("Function not allowed")
builtins.eval = restricted
builtins.exec = restricted
注意:这种方式可能不适用于所有环境(例如嵌入式解释器或某些沙箱)。
✅ 示例 3:添加自定义内置函数或常量
import builtins
def hello():
print("Hello from builtins!")
builtins.hello = hello
hello() # 可以直接调用,无需导入
这相当于给整个 Python 环境注入了一个全局函数,任何模块都可以直接调用它。
📦 三、应用场景与项目实践
1. 调试/监控工具
- 替换
input()
、open()
、print()
等函数进行日志记录或输入输出拦截。 - 用于构建自动化测试框架或调试器。
2. 沙箱限制执行
- 删除或替换危险函数(如
eval
,exec
,__import__
)。 - 示例项目:
- RestrictedPython
- Hy(Lisp 方言,在 builtins 上做了大量定制)
3. 代码混淆/保护
- 通过替换
__name__
,__file__
等魔法变量,隐藏真实模块信息。 - 一些商业加密工具会利用此技术。
4. DSL 或领域语言扩展
- 添加新的内置函数或语法糖。
- 例如:宏系统、DSL 编程语言、脚本引擎。
⚠️ 四、注意事项与风险
风险 | 说明 |
---|---|
兼容性问题 | 修改 builtins 可能导致第三方库行为异常。 |
作用域问题 | 不同模块中的 __builtins__ 可能不同(尤其是多解释器环境)。 |
安全性不足 | 单纯修改 builtins 并不能完全阻止恶意代码(如绕过 __builtins__ 获取原始函数)。 |
调试困难 | 如果过度修改内置行为,可能导致调试困难,建议保留原函数备份。 |
🧪 五、进阶玩法
1. 使用 AST + builtins 构建 DSL
结合 AST 解析和 builtins 替换,你可以构建一个小型的领域特定语言(DSL):
import ast
import builtins
class MyTransformer(ast.NodeTransformer):
def visit_Name(self, node):
if node.id == 'println':
return ast.Name(id='print', ctx=node.ctx)
return node
code = "println('Hello DSL')"
tree = ast.parse(code)
new_tree = MyTransformer().visit(tree)
compiled = compile(new_tree, '<string>', 'exec')
exec(compiled, {'print': lambda x: builtins.print(f"[DSL] {x}")})
2. 安全访问原始 builtins(防止被覆盖)
有时候你希望即使别人替换了 print
,你也能访问原始版本:
import builtins
real_print = getattr(builtins, '__dict__')['print']
def evil_print(*args):
pass
builtins.print = evil_print
real_print("This will still work!") # 原始 print
📚 六、推荐学习资源
- 📘 《Fluent Python》第 9 章(关于魔法方法和 builtins)
- 📘 《Python Cookbook》第 9 章(元编程相关)
- GitHub 搜索关键词:
monkey patch builtins
replace print python
sandbox python builtins
✅ 总结
技术点 | 描述 |
---|---|
builtins 模块 | 所有内置函数/对象的来源 |
替换函数 | 如 print , input 等,可用于日志、调试 |
删除函数 | 用于构建安全沙箱 |
注入新函数 | 类似“全局函数”,任何模块都能访问 |
安全访问 | 通过 __dict__ 获取原始函数避免被覆盖 |