name 与主程序
每个 Python 模块都有一个特殊属性 __name__。它的值取决于模块是如何被加载的:直接运行脚本时 __name__ 是 "__main__",被导入时 __name__ 是模块名。利用这个特性,可以写出既可以直接运行、又可以被导入复用的模块。
直接运行 vs 导入
创建文件 hello.py:
# hello.py
print "__name__ is:", __name__
直接运行:
$ python hello.py
__name__ is: __main__
作为模块导入:
import hello
# 输出:__name__ is: hello
主程序保护
利用 __name__ 的特性,可以把测试代码或演示代码放在 if __name__ == "__main__": 块中:
# calculator.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
# 测试代码
if __name__ == "__main__":
print "Running tests..."
assert add(2, 3) == 5
assert subtract(5, 2) == 3
print "All tests passed!"
直接运行:
$ python calculator.py
Running tests...
All tests passed!
作为模块导入:
import calculator
print calculator.add(2, 3) # 5,测试代码不会执行
为什么需要这个保护
如果没有 if __name__ == "__main__",模块被导入时会执行所有顶层代码:
# bad_module.py
print "Loading module..." # 导入时就会执行
data = load_large_dataset() # 导入时就会加载数据!
import bad_module # 输出 Loading module...,同时加载大数据集
这会导致导入速度变慢、副作用不可控。好的模块应该:
- 顶层只包含定义(函数、类、常量)
- 执行代码放在
if __name__ == "__main__":中
命令行参数处理
if __name__ == "__main__": 块常用于处理命令行参数:
# copyfile.py
import sys
import shutil
def copy_file(src, dst):
shutil.copy(src, dst)
print "Copied %s to %s" % (src, dst)
if __name__ == "__main__":
if len(sys.argv) != 3:
print "Usage: python copyfile.py <source> <destination>"
sys.exit(1)
copy_file(sys.argv[1], sys.argv[2])
$ python copyfile.py a.txt b.txt
Copied a.txt to b.txt
多模块项目的主入口
大型项目通常有一个主入口文件:
# main.py
from calculator import add, subtract
from utils import load_data
def main():
data = load_data()
result = add(data[0], data[1])
print "Result:", result
if __name__ == "__main__":
main()
这样 main.py 可以直接运行,而 calculator.py 和 utils.py 可以被其他项目复用。
main 模块
当直接运行脚本时,Python 创建一个特殊的 __main__ 模块来执行它。sys.modules["__main__"] 就是这个模块:
import sys
print sys.modules["__main__"] # <module '__main__' from 'script.py'>
被导入的模块不会出现在 __main__ 中,而是以其真实名称注册。