web-dev-qa-db-ja.com

Python:sys.exitまたはSystemExitの違いと提案の使用

オンラインで読むプログラマーの中には、sys.exit、その他はSystemExitを使用します。
基本的な質問は申し訳ありません:

  1. 違いはなんですか?
  2. 関数内でSystemExitまたはsys.exitを使用する必要があるのはいつですか?

ref = osgeo.ogr.Open(reference)
if ref is None:
    raise SystemExit('Unable to open %s' % reference)

または

ref = osgeo.ogr.Open(reference)
if ref is None:
    print('Unable to open %s' % reference)
    sys.exit(-1)
40
Gianni Spear

実用的な違いはありませんが、サンプルコードには別の違いがあります-printは標準出力になりますが、例外テキストは標準エラーになります(おそらくこれが望ましいでしょう)。

23
RichieHindle

sys.exit(s)は、前者のdocstringで説明されているように、raise SystemExit(s)の短縮形です。 help(sys.exit)を試してください。したがって、サンプルプログラムのいずれか1つではなく、

sys.exit('Unable to open %s' % reference)
27
Fred Foo

SystemExitを上げることに加えて、3つの終了関数があります。

基礎となるのはos._exitで、1つのint引数が必要で、クリーンアップせずにすぐに終了します。これに触りたくなることはまずありませんが、そこにあります。

sys.exitはsysmodule.cで定義され、PyErr_SetObject(PyExc_SystemExit, exit_code);を実行するだけです。これは、実質的にSystemExitを上げるのと同じです。 SystemExitを上げるには、sys.exitLOAD_ATTRCALL_FUNCTION vs RAISE_VARARGS opcallsを必要とするため、おそらくより高速です。また、raise SystemExitはわずかに小さいバイトコード(4バイト少ない)を生成します(from sys import exitはNoneを返すことが期待されるため、sys.exitを使用すると1バイト余分になり、余分なPOP_TOPが含まれます)。

最後の終了関数はsite.pyで定義され、REPLのexitまたはquitにエイリアスされます。これは実際にはQuitterクラスのインスタンスです(したがって、カスタム__repr__を持つことができるため、おそらく最も遅い実行になります。また、SystemExitを上げる前にsys.stdinを閉じるため、 REPL。

SystemExitの処理方法については、最終的にVMがos._exitを呼び出しますが、その前に何らかのクリーンアップを行います。また、atexit._run_exitfuncs()を実行します。 atexitモジュールを介して登録されたコールバックを実行します。os._exitを呼び出すと、atexitステップが直接バイパスされます。

10
Perkins

私の個人的な好みは、少なくともSystemExitが発生し(またはさらに良い-より意味のある、十分に文書化されたカスタム例外)、可能な限り「メイン」関数の近くでキャッチされることです。それを有効な出口とみなす最後のチャンス。 sys.exitを持つライブラリ/深く埋め込まれた関数は、設計の観点からは単純に厄介です。 (一般的に、終了は可能な限り「高く」する必要があります)

6
Jon Clements

ドキュメンテーションによると、sys.exit(s)raise SystemExit(s)を効果的に実行するため、ほとんど同じです。

3
piokuc

SystemExitは例外です。これは、基本的に、プログラムに停止してエラーを発生させたいという振る舞いがあったことを意味します。 sys.exitは、プログラムから終了するために呼び出すことができる関数であり、場合によってはシステムに戻りコードを提供します。

編集:それらは確かに同じものなので、唯一の違いはプログラムの背後にあるロジックにあります。例外は、ある種の「望ましくない」動作です。関数の呼び出しは、プログラマーの観点から見ると、「標準」アクションに近いものです。

3
Cynical

違いは多くの回答によって回答されていますが、 https://mail.python.org/pipermail/python-list/2016-April/707470.html は興味深い点を示しています。

TL; DR:「通常の」例外を発生させ、SystemExitまたはsys.exitスクリプトのトップレベルでのみ。

私はpython 2.7とLinuxで、sys.exit(1)をraise SystemExitに置き換えることができれば、簡単なコードの提案が必要です。

==実際のコード==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
     except Exception as e:
       logging.exception(e)
       sys.exit(EXIT_STATUS_ERROR)

if __== '__main__':    main()

==変更されたコード==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
    except Exception as e:
       logging.exception(e)
       raise SystemExit

if __== '__main__':    
    main()

私はこれらの両方に個人的に反対です。私の好みのパターンはこのようなものです:

  def main(argv):
    try:
      ...
    except Exception as e:
      logging.exception(e)
      return 1

  if __== '__main__':
    sys.exit(main(sys.argv))

Main()は通常の戻り値を持つ通常の関数に戻っていることに注意してください。

また、私たちのほとんどは「例外を除く」を避け、バブル以外のトップレベルだけをバブルアウトさせます。そのようにして、デバッグ用のスタックバックトレースを取得します。私はそれが例外のログを防ぎ、コンソール出力をugいものにすることに同意しますが、私はそれが勝利だと思います。そして、あなたがdo例外をログに記録したい場合、常にこれがあります:

try:... eの例外を除く:logging.exception(e)raise

例外をログに記載し、それでも正常にバブルするようにします。

「例外を除く」パターンの問題は、hidesevery例外。理解できる特定の例外の狭いセットだけではありません。

最後に、むき出しのExceptionクラスを発生させることは嫌われます。 In python 3実際には禁止されていると思われるので、とにかく移植性はありません。しかし、In Pythonでも例外インスタンスではなく、クラス:

systemExit(1)を発生させます

  1. Tryブロック内のすべての関数には、raiseを使用して例外がバブルアウトされています

    Create_logdir()の例は関数定義です

def create_logdir():

try:os.makedirs(LOG_DIR)OSError eを除く:sys.stderr.write( "ログディレクトリの作成に失敗しました...終了!!!")raise print "log file:" + destroy_log return True

def main():try:create_logdir()例外を除くe:logging.exception(e)raise SystemExit

(a)create_logdir()が失敗した場合、以下のエラーが表示されます。これで問題ありませんか、このコードを改善する必要がありますか。

ログディレクトリの作成に失敗しました...終了!!!エラー:ルート:[Errno 17]ファイルが存在します: '/ var/log/dummy'

トレースバック(最後の最後の呼び出し):ファイル「corrupt_test.py」、行245、メインcreate_logdir()ファイル「corrupt_test.py」、行53、create_logdir os.makedirs(LOG_DIR)ファイル「/ usr/local/lib/python2.7/os.py "、行157、makedirs OSError:[Errno 17]ファイルが存在します: '/ var/log/dummy'

私はおそらくあなたがやったようにログや警告メッセージを使用して、バブルアウトのアプローチを好む:

logging.exception( "create_logdir failed:makedirs(%r):%s"%(LOG_DIR、e))raise

(ログメッセージがより多くのコンテキストを記録するわけでもありません。コンテキストは問題をデバッグするときに非常に役立ちます。)

非常に小さなスクリプトの場合、sys.stderr.writeは問題ありませんが、一般に、一般的に有用であることが判明した関数はすべて、再利用するためにライブラリに移行できます。 stderrは常にメッセージの場所ではないことを考慮してください。代わりに、error()、wanr()、またはexception()を適宜使用してロギングモジュールを読み取ります。出力を内部機能に配線せずに、出力がそのように進む場所を構成するためのより多くのスコープがあります。

  1. SystemExitまたはsys.exit(1)の代わりに、単にraiseすることはできますか。これは私には間違っているように見える

    def main():

    try:create_logdir()例外を除き、logging.exception(e)を上げる

これは私自身がやることです。

考えてみてください:例外は「処理された」のでしょうか。つまり、予期されていたという理由で対処されたのでしょうか。そうでない場合は、プログラムで認識される何かnotが発生したことをユーザーが認識できるように、例外を吹き出します。

最後に、最も外側のmain()関数以外の内部からSystemExitまたはsys.exit()を実行するのは一般的に悪いことです。そして、私もそこに抵抗します。メイン関数は、適切に記述されていれば、他の場所から便利に呼び出されることが多く、それによりライブラリ関数になります(再利用されています)。そのような関数は、プログラムを一方的に中止すべきではありません。失礼ですね!代わりに、例外をバブルアウトさせてください。おそらく、main()のcallerは、それを予期して処理できます。 「レイズ」ではなく中止することで、例外を処理するのに十分なコンテキストを自分(つまり「メイン」)が知らない場合でも、呼び出し元から適切な何かをする機会を奪います。

だから私は自分自身を「育てる」ためです。そして、エラーをログに記録したいだけです。例外をログに記録したくない場合は、try/exceptentirelyを回避し、より簡単なコードにすることができます。呼び出し元に未処理の例外を心配させます。

0
serv-inc