if __name__ == "__main__": 做什么?

新手上路,请多包涵

这是做什么的,为什么要包含 if 语句?

 if __name__ == "__main__":
    print("Hello, World!")


这个问题解释了代码的作用以及它是如何工作的。如果您试图关闭某人应该使用此成语而不是使用此成语的问题,请考虑将其关闭为 为什么 Python 在我导入它时运行我的模块,以及如何停止它? 反而。

原文由 Devoted 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 538
2 个回答

简答

它是样板代码,可防止用户在无意中意外调用脚本。以下是脚本中省略守卫时的一些常见问题:

  • 如果您在另一个脚本中导入无防护脚本(例如 import my_script_without_a_name_eq_main_guard ),那么后一个脚本将触发前者 在导入时 运行并 _使用第二个脚本的命令行参数_。这几乎总是一个错误。

  • 如果您在无保护脚本中有一个自定义类并将其保存到一个 pickle 文件中,那么在另一个脚本中将其取消腌制将触发无保护脚本的导入,与上一个项目符号中概述的问题相同。

长答案

为了更好地理解这为什么以及如何重要,我们需要退后一步来了解 Python 如何初始化脚本以及它如何与其模块导入机制进行交互。

每当 Python 解释器读取源文件时,它会做两件事:

  • 它设置了一些特殊变量,例如 __name__ ,然后

  • 它执行在文件中找到的所有代码。

让我们看看它是如何工作的,以及它与您关于 __name__ 我们经常在 Python 脚本中看到的检查的问题有何关系。

代码示例

让我们使用一个稍微不同的代码示例来探索导入和脚本是如何工作的。假设以下内容位于名为 foo.py 的文件中。

 # Suppose this is foo.py.

print("before import")
import math

print("before function_a")
def function_a():
    print("Function A")

print("before function_b")
def function_b():
    print("Function B {}".format(math.sqrt(100)))

print("before __name__ guard")
if __name__ == '__main__':
    function_a()
    function_b()
print("after __name__ guard")

特殊变量

当 Python 解释器读取一个源文件时,它首先定义了一些特殊的变量。在这种情况下,我们关心 __name__ 变量。

当你的模块是主程序时

如果您将模块(源文件)作为主程序运行,例如

python foo.py

解释器会将硬编码字符串 "__main__" 分配给 __name__ 变量,即

# It's as if the interpreter inserts this at the top
# of your module when run as the main program.
__name__ = "__main__"

当您的模块被另一个模块导入时

另一方面,假设某个其他模块是主程序,它会导入您的模块。这意味着在主程序中或在主程序导入的其他模块中存在这样的语句:

 # Suppose this is in some other main program.
import foo

解释器将搜索您的 foo.py 文件(以及搜索其他一些变体),并且在执行该模块之前,它会将名称 "foo" 从导入语句分配给 __name__ 变量,即

# It's as if the interpreter inserts this at the top
# of your module when it's imported from another module.
__name__ = "foo"

执行模块的代码

设置特殊变量后,解释器执行模块中的所有代码,一次一条语句。您可能希望在代码示例旁边打开另一个窗口,以便您可以按照此说明进行操作。

总是

  1. 它打印字符串 "before import" (不带引号)。

  2. 它加载 math 模块并将其分配给名为 math 的变量。这相当于将 import math 替换为以下内容(注意 __import__ 是 Python 中的一个低级函数,它接受一个字符串并触发实际导入):

 # Find and load a module given its string name, "math",
# then assign it to a local variable called math.
math = __import__("math")

  1. 它打印字符串 "before function_a"

  2. 它执行 def 块,创建一个函数对象,然后将该函数对象分配给一个名为 function_a 的变量。

  3. 它打印字符串 "before function_b"

  4. 它执行第二个 def 块,创建另一个函数对象,然后将其分配给一个名为 function_b 的变量。

  5. 它打印字符串 "before __name__ guard"

仅当您的模块是主程序时

  1. 如果您的模块是主程序,那么它将看到 __name__ 确实设置为 "__main__" 并调用这两个函数,打印字符串 "Function A""Function B 10.0"

仅当您的模块被另一个模块导入时

  1. 相反)如果您的模块不是主程序而是由另一个程序导入,那么 __name__ 将是 "foo" ,而不是 "__main__" ,它会跳过 if 语句的主体。

总是

  1. 在这两种情况下,它将打印字符串 "after __name__ guard"

概括

总而言之,以下是两种情况下打印的内容:

 # What gets printed if foo is the main program
before import
before function_a
before function_b
before __name__ guard
Function A
Function B 10.0
after __name__ guard

 # What gets printed if foo is imported as a regular module
before import
before function_a
before function_b
before __name__ guard
after __name__ guard

为什么它会这样工作?

你可能自然想知道为什么有人会想要这个。好吧,有时您想编写一个 .py 文件,该文件既可以被其他程序和/或模块用作模块,也可以作为主程序本身运行。例子:

  • 您的模块是一个库,但您希望有一个脚本模式,它可以运行一些单元测试或演示。

  • 您的模块仅用作主程序,但它有一些单元测试,并且测试框架通过导入 .py 文件(如您的脚本)并运行特殊测试功能来工作。您不希望它仅仅因为它正在导入模块而尝试运行脚本。

  • 您的模块主要用作主程序,但它也为高级用户提供了对程序员友好的 API。

除了这些示例之外,在 Python 中运行脚本只是设置一些魔术变量并导入脚本,这很优雅。 “运行”脚本是导入脚本模块的副作用。

深思熟虑

  • 问:我可以有多个 __name__ 检查块吗?答:这样做很奇怪,但语言不会阻止你。

  • 假设以下内容在 foo2.py 中。如果你在命令行上说 python foo2.py 会发生什么?为什么?

 # Suppose this is foo2.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

def function_a():
    print("a1")
    from foo2 import function_b
    print("a2")
    function_b()
    print("a3")

def function_b():
    print("b")

print("t1")
if __name__ == "__main__":
    print("m1")
    function_a()
    print("m2")
print("t2")


  • 现在,弄清楚如果删除 __name__ 签入 foo3.py 会发生什么:
 # Suppose this is foo3.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

def function_a():
    print("a1")
    from foo3 import function_b
    print("a2")
    function_b()
    print("a3")

def function_b():
    print("b")

print("t1")
print("m1")
function_a()
print("m2")
print("t2")

  • 当用作脚本时,它会做什么?当作为模块导入时?
 # Suppose this is in foo4.py
__name__ = "__main__"

def bar():
    print("bar")

print("before __name__ guard")
if __name__ == "__main__":
    bar()
print("after __name__ guard")

原文由 Mr Fooz 发布,翻译遵循 CC BY-SA 4.0 许可协议

当您的脚本通过将其作为命令传递给 Python 解释器来运行时,

 python myscript.py

缩进级别为 0 的所有代码都将被执行。已定义的函数和类确实已定义,但它们的代码均未运行。与其他语言不同,没有 main() 自动运行的函数 - main() 函数隐式地是顶层的所有代码。

在这种情况下,顶级代码是 if 块。 __name__ 是一个内置变量,计算当前模块的名称。但是,如果直接运行模块(如上文中的 myscript.py ),则 __name__ 改为设置为字符串 "__main__" 。因此,您可以通过测试来测试您的脚本是直接运行还是被其他东西导入

if __name__ == "__main__":
    ...

如果你的脚本被导入到另一个模块中,它的各种函数和类定义将被导入并且它的顶级代码将被执行,但是上面的 if 子句的 then-body 中的代码不会’由于不满足条件而无法运行。作为一个基本示例,请考虑以下两个脚本:

 # file one.py
def func():
    print("func() in one.py")

print("top-level in one.py")

if __name__ == "__main__":
    print("one.py is being run directly")
else:
    print("one.py is being imported into another module")

 # file two.py
import one

print("top-level in two.py")
one.func()

if __name__ == "__main__":
    print("two.py is being run directly")
else:
    print("two.py is being imported into another module")

现在,如果您将解释器调用为

python one.py

输出将是

top-level in one.py
one.py is being run directly

如果您改为运行 two.py

 python two.py

你得到

top-level in one.py
one.py is being imported into another module
top-level in two.py
func() in one.py
two.py is being run directly

因此,当模块 one "one" "__main__" ,它的 __name__

原文由 Adam Rosenfield 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题