page contents

为什么 Python 没有 main 函数?

刚写 Python 的人,第一眼经常会觉得不对劲。Java 程序得从 main() 进去,C/C++ 也有 main(),到了 Python,写两行代码直接就跑了:

attachments-2026-09-E6KUu4no6ab7204a2f92b.png

刚写 Python 的人,第一眼经常会觉得不对劲。

Java 程序得从 main() 进去,C/C++ 也有 main(),到了 Python,写两行代码直接就跑了:

print("开始清理日志")

with open("access.log", encoding="utf-8") as f:
    rows = sum(1 for _ in f)

print("日志行数:", rows)

没有 main()。

没有入口类。

甚至连函数都没定义。

执行:

python clean_log.py

它照样从第一行往下跑。

这地方如果还拿 Java 那套“程序必须有一个 main 入口”去理解 Python,很容易越想越别扭。

Python 不是没有程序入口,而是没有强制你把入口写成一个叫 main 的函数。

Python 文件本身就能执行。

比如一个文件:

# sync_price.py

print("连接上游接口")
print("拉取商品价格")
print("更新本地缓存")

执行:

python sync_price.py

Python 解释器拿到这个文件之后,会创建一个模块环境,然后从上往下执行代码。

这里有个很关键的东西:

__name__

我排查 Python 脚本到底是“被执行了”还是“被别人导入了”,一般先打印它,不猜。

print(__name__)

直接运行:

python sync_price.py

输出:

__main__

但如果换一个文件导入它:

import sync_price

这时候 sync_price.py 里面的:

__name__

就不是 __main__ 了,而是:

sync_price

这就是 Python 入口机制里最值得搞明白的一层。

所以我们平时才会写这段:

if __name__ == "__main__":
    ...

它的意思并不神秘:

只有这个文件被直接执行时,才跑下面的代码。

比如我平时写批量校验脚本,不太喜欢把逻辑直接堆在文件最外层,通常会收一下:

from pathlib import Path


def scan_failed_jobs(log_dir: str) -> int:
    failed = 0

    for log_file in Path(log_dir).glob("*.log"):
        text = log_file.read_text(encoding="utf-8", errors="ignore")

        if "job_status=FAILED" in text:
            print(f"发现失败任务: {log_file.name}")
            failed += 1

    return failed


def main():
    failed_count = scan_failed_jobs("./logs")
    print(f"扫描完成,失败文件数: {failed_count}")


if __name__ == "__main__":
    main()

你看,这里明明又出现 main() 了。

那不是绕回来了吗?

不是。

这个 main() 是我们自己定义的,名字改成 run()、start()、execute() 都能跑:

def start():
    print("开始执行任务")


if __name__ == "__main__":
    start()

Python 根本不关心这个函数叫什么。

真正决定它是不是直接运行入口的,是:

if __name__ == "__main__":

而不是:

def main():

这俩东西我见过不少人混在一起理解。

还有一个坑更实际。

假设你写了个发送通知的模块:

# notifier.py

def send_notice(text):
    print(f"发送通知: {text}")


send_notice("服务启动")

另一个脚本只是想复用它:

from notifier import send_notice

send_notice("订单同步失败")

结果一运行:

发送通知: 服务启动
发送通知: 订单同步失败

第一条哪来的?

import notifier 的时候来的。

因为 Python 导入模块时,会执行模块最外层代码。

这种代码在小脚本里看着没什么,放到真实项目里就挺烦。导入一个模块,结果它顺手连数据库、启动线程、发消息、读配置,这种模块我第一眼就不太信。

应该把执行动作挡住:

def send_notice(text):
    print(f"发送通知: {text}")


def main():
    send_notice("服务启动")


if __name__ == "__main__":
    main()

这样别人:

from notifier import send_notice

只会加载函数,不会顺手执行 main()。

所以 if __name__ == "__main__": 真正有价值的地方,不是为了模仿 Java 搞一个“标准 main 函数”。

而是把两种角色切开:

这个文件可以自己跑。

也可以被别人当模块用。

Python 之所以这么设计,跟它本身的使用方式有关系。

Python 不只是拿来写完整应用。

你可以在交互式解释器里敲代码:

>>> 12 * 8
96

可以执行单文件脚本:

python cleanup.py

可以把文件作为模块导入:

import cleanup

还可以把一个包作为入口执行:

python -m tools

这种情况下,Python 会去执行包里的:

tools/__main__.py

比如:

# tools/__main__.py

from .report import build_report

build_report()

执行:

python -m tools

一样能启动。

所以 Python 的思路一直不是:

“程序必须找到一个名字叫 main 的函数。”

而是:

解释器先确定我要执行哪个模块,然后执行这个模块里的顶层代码。

__main__ 更准确地说,是当前顶层执行环境的名字。

这也是为什么一个最简单的 Python 程序可以只有一句:

print("hello")

它已经是完整程序了。

不过代码一旦不是临时脚本,我还是习惯主动留一个 main()。

原因也很简单。

下面这种:

config = load_config()
client = create_client(config)
records = fetch_records(client)
save_records(records)
write_report(records)

刚开始看挺爽,文件一长就不好收拾,而且后面想测试、复用、导入时,很容易被这些顶层代码反咬一口。

我更愿意写成:

def main():
    config = load_config()
    client = create_client(config)

    records = fetch_records(client)

    save_records(records)
    write_report(records)


if __name__ == "__main__":
    main()

不是 Python 要求这么干,是代码大了以后,这种边界更省事。

所以再碰到“为什么 Python 没有 main 函数”这个问题,可以把关注点从 main() 这个名字上挪开。

Python 有入口。

只不过它的入口首先是模块,不是某个固定函数。

直接执行文件时,这个模块叫 __main__;至于你愿不愿意再定义一个 main() 把业务逻辑收进去,那是代码组织问题,不是语法要求。

这俩分清,Python 的执行模型基本就顺了。

 更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2440 篇文章

作家榜 »

  1. Pack 2440 文章
  2. 轩辕小不懂 2403 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章