一尘不染

if __name__ == "__main__": 怎么办?

javascript

给定以下代码,有什么作用if __name__ == "__main__":

# Threading example
import time, thread

def myfunction(string, sleeptime, lock, *args):
    while True:
        lock.acquire()
        time.sleep(sleeptime)
        lock.release()
        time.sleep(sleeptime)

if __name__ == "__main__":
    lock = thread.allocate_lock()
    thread.start_new_thread(myfunction, ("Thread #: 1", 2, lock))
    thread.start_new_thread(myfunction, ("Thread #: 2", 2, lock))

阅读 221

收藏
2022-01-02

共1个答案

一尘不染

简答

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

  • 如果您在另一个脚本(例如import my_script_without_a_name_eq_main_guard)中导入无防护脚本,则第二个脚本将触发第一个在导入时运行并使用第二个脚本的命令行参数。这几乎总是一个错误。
  • 如果你在guardless 脚本中有一个自定义类并将其保存到pickle 文件中,那么在另一个脚本中取消它会触发guardless 脚本的导入,与上一个项目符号中概述的问题相同。

长答案

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

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

  • 它设置了一些特殊的变量,例如__name__,然后
  • 它执行文件中找到的所有代码。

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

代码示例

让我们使用稍微不同的代码示例来探索导入和脚本的工作方式。假设以下内容在名为foo.py.

# Suppose this is foo.py.

print("before import")
import math

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

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

print("before __name__ guard")
if __name__ == '__main__':
    functionA()
    functionB()
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 functionA"
  2. 它执行def块,创建一个函数对象,然后将该函数对象分配给一个名为 的变量functionA
  3. 它打印字符串"before functionB"
  4. 它执行第二个def块,创建另一个函数对象,然后将其分配给名为 的变量functionB
  5. 它打印字符串"before __name__ guard"

仅当您的模块是主程序时

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

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

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

总是

  1. 它会"after __name__ guard"在两种情况下打印字符串。

概括

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

# What gets printed if foo is the main program
before import
before functionA
before functionB
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 functionA
before functionB
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 functionA():
    print("a1")
    from foo2 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
if __name__ == "__main__":
    print("m1")
    functionA()
    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 functionA():
    print("a1")
    from foo3 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
print("m1")
functionA()
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")
2022-01-02