为什么我导入模块时Python会执行它,我该如何停止?

277

我正在编写一个Python程序,可以通过两种方式运行:第一种方式是调用python main.py,它会以友好的方式提示用户输入,然后通过程序运行用户的输入。另一种方式是调用python batch.py -file-,它将跳过所有友好的输入收集,并在单个批处理中运行整个文件的输入。

问题是当我运行batch.py时,它会从main.py导入一些变量/方法等,当它运行此代码时:

import main

在程序的第一行,它立即报错,因为它试图运行main.py中的代码。

我该如何停止Python运行我正在导入的main模块中包含的代码?


https://dev59.com/bHRC5IYBdhLWcg3wD87r - CristiFati
12个回答

365
因为这就是Python的工作方式 - 像'class'和'def'这样的关键字不是声明。相反,它们是真正实时执行的语句。如果它们没有被执行,你的模块将是空的。
惯用的方法是:
# stuff to run always here such as class/def
def main():
    pass

if __name__ == "__main__":
   # stuff only to run when not called via 'import' here
   main()

但是,它确实需要对被导入的模块进行源代码控制。


2
只是确认一下,你的注释“仅在未通过'import'调用时运行”的意思是命令要写在main()下面,对吗?还是无所谓的? - JobHunter69
2
@Goldname 如果在导入时,if语句内的代码不会被运行,但是主函数本身已经定义并准备好通过导入使用。当运行此模块时,该模块将只执行主函数,而不会在导入时执行它。这完全取决于您想要做什么。如果您不需要在其他地方使用main中的命令,请务必将它们写在if语句内。但对我来说,这看起来更整洁。 - Felix
@Felix 我认为最好的做法是始终定义一个 main,这样在其中定义的任何名称都不会出现在全局命名空间中,从而避免出现奇怪的错误,例如如果您错误地访问了自由变量的函数,并且有一个与其同名的全局变量。 - wjandrea

87
由于Python的工作方式,它在导入模块时必须运行它们。
为了防止模块中的代码在导入时被执行,而只在直接运行时执行,您可以使用以下代码进行保护:
if __name__ == "__main__":
    # this won't be run when imported

您可能希望将此代码放入一个main()方法中,这样您可以直接执行该文件,或导入模块并调用main()。例如,假设这在文件foo.py中。

def main():
    print "Hello World"

if __name__ == "__main__":
    main()

这个程序可以通过以下两种方式运行:要么使用命令 python foo.py,要么从另一个Python脚本中调用:

import foo

...

foo.main()

20

使用 if __name__ == '__main__' 惯用语 -- __name__ 是一个特殊变量,如果模块被作为脚本运行,则其值为 '__main__',如果是被导入,则为模块名。所以你需要像下面这样做:

# imports
# class/function definitions
if __name__ == '__main__':
    # code here will only run when you invoke 'python main.py'

6

很遗憾,你不能这样做。这是导入语法的一部分,它非常重要 -- 记住 def 实际上是被执行的内容,如果 Python 没有执行导入操作,你将无法使用函数。

然而,由于你可能可以访问该文件,所以你可以尝试查看并确定错误的原因。可能可以修改环境来防止错误发生。


1
作为一条提示:如果没有办法修改环境以防止错误发生,也许你应该使用不同的模块。 - cwallenpoole
我只是想确认一下,根据你的回答。我是对的吗?当我们导入时,解释器会看到def并将函数体分配给函数名称,但不会执行它。这是正确的吗? - Green Falcon
1
@GreenFalcon 是的。只有在调用 foo() 之后,def foo(): doSomthingWakciy() 才会中断。 - cwallenpoole

5

将代码放入函数中,直到调用该函数才会执行。在您的main.py中应该有一个主函数,其中包含以下语句:

if __name__ == '__main__':
  main()

如果执行 python main.py 命令,将会运行 main() 函数。但如果导入 main.py,则不会运行此函数。为了更加清晰易懂,你应该将 main.py 重命名。


4

有一个名为 PEP 299 的Python增强提案,旨在用 def __main__: 取代 if __name__ == '__main__': 习惯用法,但该提案被拒绝了。仍然可以阅读以了解在使用 if __name__ = '__main__': 时要注意什么。


3
您可以像这样编写您的“main.py”文件:
#!/usr/bin/env python

__all__=["somevar", "do_something"]

somevar=""

def do_something():
    pass #blahblah

if __name__=="__main__":
    do_something()

3

我进行了一项简单的测试:

#test.py

x = 1
print("1, has it been executed?")


def t1():
     print("hello")
     print("2, has it been executed?")


def t2():
     print("world")
     print("3, has it been executed?")


def main():
     print("Hello World")
     print("4, has it been executed?")


print("5, has it been executed?")
print(x)

# while True:
# t2()

if x == 1:
     print("6, has it been executed?")

#test2.py

import test

执行或运行test2.py时,运行结果如下:

1, has it been executed?

5, has it been executed?

1

6, has it been executed?

结论:当导入的模块没有添加if __name__=="__main__":时,当前模块将被运行。导入模块中不在函数中的代码会按顺序执行,而函数中的代码只有在被调用时才会执行。
此外:
def main():
    # Put all your code you need to execute directly when this script run directly.
    pass

if __name__ == '__main__':
    main() 
else:
    # Put functions you need to be executed only whenever imported

0
另一个选择是使用二进制环境变量,例如我们称之为“run_code”。如果run_code = 0(False),则结构main.py将绕过代码(但是暂时绕过的函数仍将作为模块导入)。稍后当您准备好使用已导入的函数(现在是模块)时,请将环境变量run_code = 1(True)。使用os.environ命令设置和检索二进制变量,但一定要在检索时将其转换为整数(或重新构造if语句以读取字符串值)。
在main.py中:
import os

#set environment variable to 0 (False):
os.environ['run_code'] = '0'


def binary_module():
    #retrieve environment variable, convert to integer
    run_code_val = int(os.environ['run_code'] )
    
    if run_code_val  == 0:
        print('nope. not doing it.')
    if run_code_val == 1:
        print('executing code...')
        # [do something]

...在加载main.py的任何脚本中:

import os,main

main.binary_module() 

输出:不行,不做。

# now flip the on switch!
os.environ['run_code'] = '1'
main.binary_module()

输出:正在执行代码...

*注意:上述代码假定main.py和导入它的任何脚本存在于同一目录中。


0
一个小错误可能会发生(至少我遇到过),特别是在分发执行完整分析的Python脚本/函数时,就是在.py文件的末尾直接调用函数。 用户需要修改的仅仅是输入文件和参数。 这样做当你导入时,函数会立即运行。为了正确的行为,你只需要删除函数内部的调用,并将其保留给真正的调用文件/函数/代码部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接