(Python程式設計 | 系統程式設計 | 並行系統工具 | 程式退出)

whisperrr發表於2021-09-15

  程式退出

  正常情況下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 大連婦科醫院哪個好  

  "分支子程式,用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模組中它們通常不退出,除非子執行緒被設定為守護執行緒。而使用執行緒時,子程式通常比父程式存在的時間長。執行緒是程式內的函式呼叫,但程式的獨立性和自主性更強一些的話。

  大多是指令碼是在執行完原始碼的最後一行後退出的,而大多數執行緒函式僅僅執行返回操作;顯示退出呼叫一般僅對於例外情況適用,而且適用情景不多。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70005147/viewspace-2792258/,如需轉載,請註明出處,否則將追究法律責任。

相關文章