python 脚本解析 1----main()写法和几种退出方法

main()主函数:

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()

这样写的灵活性还不够高,尤其是需要解析复杂的命令行选项时。

1、采用可选的 argv 参数

首先,修改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=sys.argv

「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')

2、定义一个Usage()异常,简化参数导致的异常抛出

另一个改进之处,就是定义一个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。然后,这可以用于预期的错误,例如无法打开必要的文件。

这些错误不是命令行语法错误,但仍是预期的,并且回溯又感觉不太友好。

Python的退出方式:quit(), exit(), sys.exit() and os._exit()

quit()&&exit()

“exit()”或“quit()”,这两个命令是等价的,定义在site.py中,是Python内置函数,用于正常退出程序。它在幕后引发 SystemExit 异常。可以通过Ctrl-DCtrl-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([arg]) :

说白了就是一个从程序外部获取参数的桥梁。更适用于生产环境。sys.exit() 常用于在主线程中退出。在Python中,线程是可以支持被父线程杀死的,如果想要在线程中退出程序,可以使用sys.exit()函数来实现。

首先我们需要import sys,sys是python3的一个标准库,也就是一个官方的模块。可选参数 arg 可以是给出退出的整数或其他类型的对象。

sys.exit(n)会引发一个异常:SystemExit,可以捕获异常执行些清理工作。此处用于捕获模块执行结果,如果这个异常没有被捕获,那么python解释器将会退出。

  • 如果进程正常退出,则退出代码将为0:sys.exit(0) & sys.exit()
  • 如果进程因未捕获的异常而终止,则退出代码将为 1-127:sys.exit(1)
  • 如果进程仍在运行,则退出代码将为None

 注意:也可以将字符串传递给 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()

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

你可能感兴趣的:(python)