文章目录
- 程序退出
- `sys`模块退出
- `os`模块退出
- *Shell*命令退出状态代码
- 用`os.system`和`os.popen`获得退出状态
- 输出流缓冲:初次介绍
- 用`subprocess`获得退出状态
- 进程的退出状态和共享状态
- 线程的退出状态和共享状态
程序退出
正常情况下Python脚本在程序末尾退出,我们也可以通过sys
和os
模块里的工具显示地调用程序退出。
sys
模块退出
sys.exit(N)
抛出一个内建的SystemExit
异常,并以状态N退出。
我们可以捕捉异常以拦截过早退出:
>>> try:
... sys.exit()
... except SystemExit:
... print('ignoring exit')
...
ignoring exit
事实上,用Python的raise
语句显示地抛出内建SystemExit
异常和调用sys.exit()
效果是一样的。实践中更有用的是try
代码块捕捉程序其他部分抛出的退出异常。
示例:test_exit_sys.py
#!/usr/bin/env python
"测试sys.exit"
import sys
def later():
print('Bye sys world')
sys.exit(11)
print('Never reached')
if __name__ == '__main__':
later()
输出:test_exit_sys.py
Bye sys world
载入later
函数的程序可以将其退出异常捕获并重写,或者编写一个负责清理的finally
代码块:
>>> from test_exit_sys import later
>>>
>>> try:
... later()
... except SystemExit:
... print('Ignoring...')
...
Bye sys world
Ignoring...
>>>
>>> try:
... later()
... finally:
... print('Clean up')
...
Bye sys world
Clean up
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/python-programming---markdown-notes/my_PP4E/system$ # 交互对话进程退出
os
模块退出
在Unix下的分支进程中,我们通常调用os_exit
函数退出。
对于os_exit
,调用进程立即退出,而不是抛出可以捕获或忽略的异常。事实上,进程退出时也不输出流缓冲和运行清理处理器,所以这种做法一般应当只在分支出的子进程上进行,而最好不要用于整个程序的退出行为。
示例:test_exit_os.py
#!/usr/bin/env python
"测试os._exit"
import os
def out_here():
print('Bye os world')
os._exit(11)
print('Never reach')
if __name__ == '__main__':
out_here()
输出:test_exit_os.py
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/python-programming---markdown-notes/my_PP4E/system$ ./test_exit_os.py
Bye os world
和sys.exit
不同,try
/except
和try
/finally
对os._exit
均不起作用。
Shell命令退出状态代码
sys.exit
和os._exit
都接受退出状态代码作为参数(在sys
模块调用中为可选,但在os
模块调用中为必需)。
在Linux下,我们询问status
这个shell变量以获得上一个程序的退出状态,通常约定以非零的数值表示出现了某种问题:
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/python-programming---markdown-notes/my_PP4E/system$ ./test_exit_sys.py
Bye sys world
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/python-programming---markdown-notes/my_PP4E/system$ echo $status
11
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/python-programming---markdown-notes/my_PP4E/system$ ./test_exit_os.py
Bye os world
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/python-programming---markdown-notes/my_PP4E/system$ echo $status
11
在启动Shell命令时,可以这样提供退出状态:
-
os.system
调用的返回值 -
os.popen
对象的close
方法的返回值(由于历史原因,如果退出状态是0则返回None
) -
subprocess
模块中的多种接口(如果call
函数的返回值,Popen
对象的returncode
属性和wait
方法的结果)
通过分支进程运行程序时,退出状态可在父进程中通过os.wait
和os.waitpid
调用获知。
用os.system
和os.popen
获得退出状态
下面的例子在Linux上运行:
>>> pipe = os.popen('python test_exit_sys.py')
>>> pipe.read()
'Bye sys world\n'
>>> stat = pipe.close() # 返回退出状态代码
>>>
>>> stat
2816
>>> hex(stat)
'0xb00'
>>> stat >> 8 # 在类Unix系统下从位掩码中提取出退出状态
11
- 在这种类Unix平台上使用
os.popen
,退出状态实际上被包装进返回值的特定比特位置;它的确在那里,但我们需要将结果右移8比特才能读出它。
os.system
执行的命令直接通过Python库发回状态信息:
>>> os.system('./test_exit_sys.py')
Bye sys world
2816
>>> stat = os.system('./test_exit_sys.py')
Bye sys world
>>> stat
2816
>>> stat, hex(stat), stat >> 8
(2816, '0xb00', 11)
输出流缓冲:初次介绍
如果需要输出流无缓冲,可以使用-u
Python命令行标识符运行目标脚本,或者使用sys.stdout.flush
更改脚本以手动将内部缓冲区中的数据立刻写入文件。不然,调用os._exit
立刻关闭时打印到标准输出流中的文本可能没从缓冲里冲洗出去。在默认模式下,标准输出流在连接到popen
类的管道时是全缓冲的;如果连接到终端时,则仅进行行缓冲。
>>> import os
>>>
>>> pipe = os.popen('./test_exit_os.py')
>>> pipe.read()
''
>>>
>>> pipe = os.popen('python -u test_exit_os.py')
>>> pipe.read()
'Bye os world\n'
你可以在os.popen
和subprocess.Popen
中传入模式和缓存参数以指定行缓冲,不过在这个示例中却没有用,因为传入这些工具的参数属于调用进程的管道输入端,而不属于派生进程的输出流:
>>> pipe = os.popen('./test_exit_os.py', 'r', 1)
>>> pipe.read()
''
>>>
>>> from subprocess import Popen, PIPE
>>>
>>> pipe = Popen('./test_exit_os.py', shell=True, bufsize=1, stdout=PIPE)
>>> pipe.stdout.read()
b''
用subprocess
获得退出状态
>>> from subprocess import Popen, PIPE, call
>>>
>>> pipe = Popen('./test_exit_sys.py', shell=True, stdout=PIPE)
>>> pipe.stdout.read()
b'Bye sys world\n'
>>> pipe.wait()
11
>>>
>>> call('./test_exit_sys.py', shell=True)
Bye sys world
11
>>>
>>> pipe = Popen('./test_exit_sys.py', shell=True, stdout=PIPE)
>>> pipe.communicate()
(b'Bye sys world\n', None)
>>> pipe.returncode
11
- 在类Unix平台下,与
os.popen
不同的是,它的退出状态没有被编码。
进程的退出状态和共享状态
示例:test_exit_fork.py
#!/usr/bin/env python
"分支子进程,用os.wait观察其退出状态"
import os
EXIT_STAT_INT = 0
def child():
"子进程"
global EXIT_STAT_INT
EXIT_STAT_INT += 1
print('Hello from child', os.getpid(), EXIT_STAT_INT)
os._exit(EXIT_STAT_INT)
def main():
"父进程"
while True:
new_pid_int = os.fork()
if new_pid_int == 0:
child()
else:
pid_int, status_int = os.wait()
print('Parent got', pid_int, status_int, status_int >> 8)
if input() == 'q':
break
if __name__ == '__main__':
main()
输出:test_exit_fork.py
Hello from child 18469 1
Parent got 18469 256 1
Hello from child 18475 1
Parent got 18475 256 1
Hello from child 18476 1
Parent got 18476 256 1
q
线程的退出状态和共享状态
示例:test_exit_thread.py
#!/usr/bin/env python
"派生子线程,查看其返回状态和共享状态"
import _thread as thread
EXIT_STAT_INT = 0
def child():
"子线程"
global EXIT_STAT_INT
EXIT_STAT_INT += 1
thread_id_int = thread.get_ident()
print('Hello from child', thread_id_int, EXIT_STAT_INT)
thread.exit()
print('Never reach')
def main():
while True:
thread.start_new_thread(child, ())
if input() == 'q':
break
if __name__ == '__main__':
main()
输出:test_exit_thread.py
Hello from child 140330165782080 1
Hello from child 140330165782080 2
Hello from child 140330165782080 3
q
- 这个显示的是在Ubuntu下运行的结果。
- Python每次创建的线程标识符都不一样。因为它们是随机生成的,但在所有运行着的活动线程中具有唯一性,因此可作为字典键以保存每个线程的信息(在某些平台上线程的id可以在其退出后再次使用)。
- 在某些系统平台下如果
print
和input
有可能发生流访问交叠的话,那么也需要同步化。 - 线程通常在其运行的函数返回后默默地退出,我们也可以显示地调用
_thread.exit
是线程终止,它和sys.exit
基本上一样,都是抛出SystemExit
异常。 - 备选的线程
threading
模块没有相当于_thread.exit
的方法,但也可用raise SystemExit
或sys.exit
等达到相同的效果。
复习一下,两个线程模型的行为有所不同:在_thread
中,大多数平台上的程序随其父线程的退出而退出,但在threading
模块中它们通常不退出,除非子线程被设置为守护线程。而使用线程时,子进程通常比父进程存在的时间长。线程是进程内的函数调用,但进程的独立性和自主性更强一些的话。
大多是脚本是在运行完源代码的最后一行后退出的,而大多数线程函数仅仅执行返回操作;显示退出调用一般仅对于例外情况适用,而且适用情景不多。