Python之父Guido van Rossum提供了一些关于如何编写易于在其他上下文中调用的main() 函数的建议,例如,当您想尝试时从交互式Python 提示符中调用。
python主函数的一般写法:
""Module docstring.
This serves as a long usage message.
"""
import sys
import getopt
def main():
# parse command line options
try:
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
except getopt.error, msg:
print msg
print "for help use --help"
sys.exit(2)
# process options
for o, a in opts:
if o in ("-h", "--help"):
print __doc__
sys.exit(0)
# process arguments
for arg in args:
process(arg) # process() is defined elsewhere
if __name__ == "__main__":
main()
这样写的灵活性还不够高,尤其是需要解析复杂的命令行选项时。
首先,修改main()函数,使其接收一个可选参数 argv,支持在交互式shell中调用该函数:
def main(argv=None):
if argv is None:
argv = sys.argv # etc., replacing sys.argv with argv in the getopt() call.
上面的写法动态地提供 argv 的值,这比写成argv=sys.argv更加的灵活,
def main(argv=sys.argv): # etc.
因为在调用函数时,sys.argv 的值可能会发生变化。而默认参数的值永远在定义main()函数时就决定了。
但是现在sys.exit()
函数调用会产生问题:当main()
函数调用sys.exit()
时,交互式解释器就会推出!解决办法是让main()
函数的返回值指示退出状态(exit status)。因此,最后面的那行代码就变成了这样:
if __name__ == "__main__":
sys.exit(main())
main() 中对 sys.exit(n) 的调用都变成了 return n
「argv」是「argument variable」参数变量的简写形式,一般在命令行调用的时候由系统传递给程序。这个变量其实是一个List列表,argv[0] 一般是被调用的脚本文件名或全路径,和操作系统有关,argv[1]和以后就是传入的数据了。
因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv是传递给 Python 脚本的命令行参数列表,所以能用[]提取其中的元素。其第一个元素argv[0]是程序本身,即sys.argv[0]是当前所执行的脚本,index 1以后的才是所传入的参数,用sys.argv[1:]可以获取到所有的参数,并且输出到一个列表里面。而切片获取的参数类型为字符串,即sys.argv传入的参数为字符串类型,如果想做一些条件判断的话需要转成所需要的数据类型。
如果该命令是使用解释器的 -c 命令行选项执行的,则 argv[0] 将设置为字符串“-c”。如果没有脚本名称传递给 Python 解释器,则 argv[0] 为空字符串。
# test.py
import sys
print(sys.argv[0])
print(type(sys.argv))
for index,arg in enumerate(sys.argv):
print(index,arg)
#python test.py 1 2
#result:
#test.py
#
#(0, 'test.py')
#(1, '1')
#(2, '2')
另一个改进之处,就是定义一个Usage()异常,可以在main()
函数最后的except语句
捕捉该异常:
import sys
import getopt
class Usage(Exception):
def __init__(self, msg):
self.msg = msg
def main(argv=None):
if argv is None:
argv = sys.argv
try:
try:
opts, args = getopt.getopt(argv[1:], "h", ["help"])
except getopt.error, msg:
raise Usage(msg)
# more code, unchanged
except Usage, err:
print >>sys.stderr, err.msg
print >>sys.stderr, "for help use --help"
return 2
if __name__ == "__main__":
sys.exit(main())
这样main()
函数就只有一个退出节点(exit)了,这比多个 return 2 语句的做法要好。而且,参数解析重构起来也更容易:在辅助函数中引发Usage
的问题不大,但是使用return 2
却要小心处理返回值传递的问题。
如果考虑将try / except子句从main()函数移出,将其包含在模块末尾的代码中(if__name__ ==“__ main__”:......),但这意味着 当你以交互方式调用main()时,但这会引发syntax errors,这不是很有用。
如果选择另一种方式,比如说,再定义另一个异常,可能称为Error
,它的处理方式与此类似Usage
,但返回 1。然后,这可以用于预期的错误,例如无法打开必要的文件。
这些错误不是命令行语法错误,但仍是预期的,并且回溯又感觉不太友好。
“exit()”或“quit()”,这两个命令是等价的,定义在site.py中,是Python内置函数,用于正常退出程序。它在幕后引发 SystemExit 异常。可以通过Ctrl-D
或Ctrl-Z
来调用quit()
函数。
在很多类型的操作系统里,exit(0) 可以中断某个程序,而其中的数字参数则用来表示程序是否是碰到错误而中断。exit(1) 表示发生了错误,而 exit(0) 则表示程序是正常退出。当正常调用这两个函数时,Python解释器会退出并返回0作为退出代码。
这和布尔逻辑 0==False 正好相反,可以用不一样的数字表示不同的错误结果。比如你可以用exit(2)、exit(1) 表示不同的错误。
print(quit):
Use quit() or Ctrl-D (i.e. EOF) to exit
def setquit():
"""Define new builtins 'quit' and 'exit'.
These are objects which make the interpreter exit when called.
The repr of each object contains a hint at how it works.
"""
if os.sep == '\\':
eof = 'Ctrl-Z plus Return'
else:
eof = 'Ctrl-D (i.e. EOF)'
builtins.quit = _sitebuiltins.Quitter('quit', eof)
builtins.exit = _sitebuiltins.Quitter('exit', eof)
说白了就是一个从程序外部获取参数的桥梁。更适用于生产环境。sys.exit() 常用于在主线程中退出。在Python中,线程是可以支持被父线程杀死的,如果想要在线程中退出程序,可以使用sys.exit()函数来实现。
首先我们需要import sys,sys是python3的一个标准库,也就是一个官方的模块。可选参数 arg 可以是给出退出的整数或其他类型的对象。
sys.exit(n)会引发一个异常:SystemExit,可以捕获异常执行些清理工作。此处用于捕获模块执行结果,如果这个异常没有被捕获,那么python解释器将会退出。
注意:也可以将字符串传递给 sys.exit() 方法:sys.exit('Broken')
import sys
time = 8
if time > 8:
sys.exit("time is more than 8")
else:
print("You are late!")
#result:
# An exception has occurred, use %tb to see the full traceback.
#SystemExit:time is more than 8
需要注意的是,如果在多线程或多进程应用程序中使用sys.exit()
函数时,可能会导致程序无法完全退出或其他问题。在这种情况下,最好使用os._exit()
函数来终止进程。
os._exit()会直接将python程序终止,之后的所有代码都不会继续执行。
os._exit()方法用于以指定状态退出进程,而不调用清理处理程序、刷新 stdio 缓冲区等。 所以,它的作用是立即退出程序,不会执行任何清理工作,也不会抛出任何异常,是一种类似于C语言中的exit()函数的底层退出方式。
一般来说os._exit() 用于在线程中退出,sys.exit() 用于在主线程中退出。
import os
try:
os._exit(0)
except:
print("jieshu")
#result:
不会打印“jieshu”
try:
sys.exit(0)
except:
print('jieshu')
finally:
print('jixu')
#result:
#jieshu
#jixu
区别:sys.exit()的退出会引发SystemExit异常,可以捕获此异常做清理工作。os._exit()直接将python解释器退出,余下的语句不会执行。
一般情况下使用sys.exit()即可,一般os._exit()方法通常在 os.fork() 系统调用后的子进程中使用,os._exit() 仅适用于需要立即退出的特殊情况。如果父进程有一个或多个正在运行的子进程,则在父进程中调用sys.exit()不会终止该主进程。原因是如果主进程有一个或多个非守护程序子进程正在运行,则主进程不会终止(在本例中就是如此)。
sys.exit()会等到子进程运行结束了,在终止主进程,但是调用os._exit()则会直接终止主进程,而子进程继续执行。
替换倒二行,以查看两个退出方法对进程的影响。
from time import sleep
from multiprocessing import Process
from multiprocessing import parent_process
import sys,os
# function executed in a new process
def task():
print('Child process running', flush=True)
sleep(2)
parent = parent_process()
print(f'Alive: {parent.is_alive()}', flush=True)
print(f'Exitcode: {parent.exitcode}')
# entry point
if __name__ == '__main__':
child = Process(target=task)
child.start()
sleep(1)
print('Exiting...')
sys.exit() # os._exit(os.EX_OK)
print('Never gets here...')
【参考文档】
Exit a Process with sys.exit() in Python:https://superfastpython.com/exit-process/
Python main() functions: https://www.artima.com/weblogs/viewpost.jsp?thread=4829
Python exit commands: https://www.geeksforgeeks.org/python-exit-commands-quit-exit-sys-exit-and-os-_exit/
The Python Standard Library — Python 3.11.4 documentation