(Python编程 | 系统编程 | 并行系统工具 | 程序退出)

  程序退出

  正常情况下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)

  输出流缓冲:初次介绍

  如果需要输出流无缓冲,可以使用-uPython命令行标识符运行目标脚本,或者使用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 大连妇科医院哪个好  http://www.83692222.cn/

  "分支子进程,用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模块中它们通常不退出,除非子线程被设置为守护线程。而使用线程时,子进程通常比父进程存在的时间长。线程是进程内的函数调用,但进程的独立性和自主性更强一些的话。

  大多是脚本是在运行完源代码的最后一行后退出的,而大多数线程函数仅仅执行返回操作;显示退出调用一般仅对于例外情况适用,而且适用情景不多。


请使用浏览器的分享功能分享到微信等