当前位置:首页>python>Python __name__ 与 __main__:一行代码,两种命运

Python __name__ 与 __main__:一行代码,两种命运

  • 2026-09-08 12:21:30
Python __name__ 与 __main__:一行代码,两种命运

Python __name__ 与 __main__:一行代码,两种命运

我是陈默,一个正拼命上岸的码农。

你有没有注意到,几乎每个 Python 项目里都有这么一行?

if __name__ == "__main__":

它看起来不起眼。但理解了它,你就理解了 Python 模块化开发的核心逻辑。

今天我们把这件事彻底讲清楚。


1. __name__ 是什么?

每个 Python 文件都有一个内置变量 __name__。它的值取决于你怎么运行这个文件。

直接运行:__name__ 就是 "__main__"

创建一个文件 hello.py:

# hello.pyprint(__name__)

直接运行:

python hello.py# 输出: __main__

当你直接运行一个文件时,Python 把它的 __name__ 设为 "__main__"。

被导入:__name__ 就是模块名

再创建一个文件 main.py:

# main.pyimport hello

运行:

python main.py# 输出: hello

同一个文件,被导入时 __name__ 变成了文件名(不含 .py)。

这就是关键区别:

运行方式
__name__
 的值
直接运行 python hello.py
"__main__"
被 import hello 导入
"hello"

2. if __name__ == "__main__" 干了什么?

它让同一段代码有两种行为:

  • 直接运行时,执行某些代码
  • 被导入时,不执行那些代码
# my_module.pydefadd(a, b):return a + bdefmultiply(a, b):return a * b# 只有直接运行时才执行测试if __name__ == "__main__":    print(add(3, 5))        # 输出: 8    print(multiply(3, 5))   # 输出: 15    print("所有测试通过!")

直接运行:

python my_module.py# 输出:# 8# 15# 所有测试通过!

被导入时:

import my_moduleprint(my_module.add(10, 20))    # 输出: 30# 不会输出 8、15、"所有测试通过"

被导入时,函数定义会加载,但 if 里面的测试代码不会执行。


3. 为什么需要它?一个真实的场景

假设你写了一个工具文件 math_tools.py:

不用 __name__ 判断

# math_tools.py(不推荐的写法)defadd(a, b):return a + bdefdivide(a, b):return a / b# 直接写了测试代码print(add(1, 2))print(divide(10, 3))

然后在另一个文件里导入它:

# main.pyfrom math_tools import addresult = add(100, 200)print(result)

运行 main.py:

python main.py# 输出:# 3            ← 这是 math_tools.py 里的测试代码!# 3.3333...    ← 这也是!# 300          ← 这才是你要的结果

问题很明显:导入的时候,测试代码跟着跑了一遍。

加上 __name__ 判断

# math_tools.py(推荐的写法)defadd(a, b):return a + bdefdivide(a, b):return a / bif __name__ == "__main__":# 只有直接运行才会测试    print(add(1, 2))        # 测试加法    print(divide(10, 3))    # 测试除法    print("测试完毕")

现在导入它:

python main.py# 输出: 300    ← 干净,只有你要的结果

这就是 if __name__ == "__main__" 的价值:让模块既能被导入复用,又能独立测试。


4. 实际应用场景

场景一:带测试的工具模块

# string_utils.pydefis_palindrome(text):"""判断是否是回文"""    cleaned = text.lower().replace(" ", "")return cleaned == cleaned[::-1]defcount_words(text):"""统计单词数"""return len(text.split())if __name__ == "__main__":# 测试回文assert is_palindrome("abcba") == Trueassert is_palindrome("hello") == Falseassert is_palindrome("A B C B A") == True    print("回文测试通过 ✓")# 测试统计assert count_words("hello world") == 2assert count_words("Python is great") == 3    print("统计测试通过 ✓")

直接运行可以验证函数是否正确。被导入时就是一个纯粹的工具库。

场景二:可配置的脚本入口

# config.pyDATABASE = {"host": "localhost","port": 3306,"user": "root",}defget_db_url():returnf"mysql://{DATABASE['user']}@{DATABASE['host']}:{DATABASE['port']}"if __name__ == "__main__":# 直接运行时打印当前配置    print("当前配置:")for key, value in DATABASE.items():        print(f"  {key}: {value}")    print(f"\n连接地址:{get_db_url()}")

被导入时只提供配置。直接运行时帮你检查配置内容。

场景三:命令行工具

# converter.pydefcelsius_to_fahrenheit(c):return c * 9 / 5 + 32deffahrenheit_to_celsius(f):return (f - 32) * 5 / 9if __name__ == "__main__":import sysif len(sys.argv) != 3:        print("用法:python converter.py <温度> <C或F>")        print("示例:python converter.py 100 C")        sys.exit(1)    value = float(sys.argv[1])    unit = sys.argv[2].upper()if unit == "C":        print(f"{value}°C = {celsius_to_fahrenheit(value):.1f}°F")elif unit == "F":        print(f"{value}°F = {fahrenheit_to_celsius(value):.1f}°C")else:        print("单位必须是 C 或 F")

直接运行是命令行工具。被导入时是普通的转换函数库。

python converter.py 100 C# 输出: 100.0°C = 212.0°Fpython converter.py 98.6 F# 输出: 98.6°F = 37.0°C

5. __name__ 的其他用法

判断当前是哪个模块

import jsonimport mathprint(json.__name__)    # 输出: jsonprint(math.__name__)    # 输出: math

每个模块都知道自己叫什么。

在包中判断入口

# mypackage/__init__.pyprint(f"包被加载了,我是 {__name__}")# 被导入时输出: 包被加载了,我是 mypackage

用 __name__ 做日志

import logginglogging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)defprocess_data(data):    logger.info(f"开始处理 {len(data)} 条数据")# ... 处理逻辑    logger.info("处理完成")

logging.getLogger(__name__) 让日志自动带上模块名,方便定位问题来源。

INFO my_module:开始处理 100 条数据INFO my_module:处理完成

6. 常见误区

误区一:__main__ 是文件名

不是。"__main__" 是 Python 的一个约定字符串,表示"这是直接运行的入口文件"。跟文件名没关系。

# 不管文件叫什么名字# 直接运行时 __name__ 永远是 "__main__"

误区二:只有入口文件才需要写

不是。每个模块都可以写。 这样每个模块都能独立测试。

# utils.pydefhelper():return"帮助"if __name__ == "__main__":    print(helper())    # 独立测试
# main.pyfrom utils import helperif __name__ == "__main__":    print(helper())    # 主程序入口

误区三:__main__ 里面的代码不能有函数

可以有任何代码。但通常把主要逻辑放在函数里,__main__ 里只做调用:

# ❌ 全堆在 __main__ 里if __name__ == "__main__":    data = load_data()    result = process(data)    save(result)# ✅ 逻辑放函数里,__main__ 只负责调用defmain():    data = load_data()    result = process(data)    save(result)if __name__ == "__main__":    main()

第二种写法的好处:main() 可以被其他模块调用。


7. 实战:完整的项目结构

my_project/├── utils/│   ├── __init__.py│   ├── file_ops.py      # 文件操作│   └── calc.py          # 计算工具└── main.py              # 入口
# utils/calc.pydefanalyze(numbers):"""分析一组数字"""return {"count": len(numbers),"sum": sum(numbers),"avg": round(sum(numbers) / len(numbers), 2) if numbers else0,"max": max(numbers) if numbers elseNone,"min": min(numbers) if numbers elseNone,    }if __name__ == "__main__":# 独立测试    test_data = [85, 92, 78, 90, 67]    result = analyze(test_data)for key, value in result.items():        print(f"{key}: {value}")
# main.pyfrom utils.calc import analyzedefmain():    scores = [85, 92, 78, 90, 67, 55, 98, 73]    result = analyze(scores)    print(f"班级人数:{result['count']}")    print(f"平均分:{result['avg']}")    print(f"最高分:{result['max']}")    print(f"最低分:{result['min']}")if __name__ == "__main__":    main()

每个文件都能独立运行测试,也能被其他文件导入使用。这就是 Python 项目的标准写法。


最后

if __name__ == "__main__" 只有一行,但它体现了一个重要的设计思想:

一个模块应该既能独立运行,又能被安全导入。

记住三件事:

  1. 直接运行时 __name__ 是 "__main__",被导入时是模块名
  2. 每个模块都可以写 if __name__ == "__main__" 做独立测试
  3. 主逻辑放函数里,__main__ 里只做调用

我的建议:

从今天起,你写的每个 .py 文件都加上 if __name__ == "__main__"。哪怕暂时不写测试代码,先把习惯养起来。

好的习惯不需要理由,就像锁门不需要理由一样。

今天就到这里。

我是陈默,我们下期再见。


如果你觉得这篇文章有帮助,欢迎关注我。我会持续分享 Python 学习的干货。

最新文章

随机文章