動的に例外をキャッチすることについて議論するときに私の心を驚かせたことの 1 つは、隠れたバグや楽しみを見つけることができるということです...
問題のコード
以下のコードは、良さそうな製品のものです抽象化コード - わずか (!) 。これは、統計データを呼び出してから処理する関数です。統計データは重大ではないため、ソケット接続を使用して値を取得します。システムの場合は、エラーを記録して続行します
(この記事は doctest を使用してテストしました。これは、コードが実行できることを意味します!)
>>> def get_stats():
.. . pass
.. .
>>> def do_something_with_stats(stats):
... pass
...
>>> try:
... stats = get_stats()
.. .socket.error を除いて:
...logging.warning("統計を取得できません")
... else:
... do_something_with_stats(stats)
Find
何も見つかりませんでしたテスト中に間違っていましたが、実際には静的分析レポートに問題があることに気付きました:
$ flake8 filename.py
filename.py:351:1: F821 unknown name 'socket'
filename.py:352:1: F821 未定義の名前 ' ロギング'
明らかに、テストしていません。問題は、コード内でソケットとロギング モジュールを参照していないことです。私が驚いたのは、事前に NameError がスローされなかったことです。これらの例外ステートメントを探してください。これらの例外をキャッチする必要があるのですが、何を知る必要がありますか?
例外ステートメントの検索は遅延して完了するだけであることがわかります。遅延検索の名前だけでなく、「引数」として例外を明示的に宣言することもできます。
これは良いことでもあり、悪いことでもあります。段落)
例外パラメータは任意の形式で数値として渡すことができます。これにより、異常な動的パラメータをキャプチャできます。
>>> def do_something():
... blob
...
>>> def Try(action,ignore_spec):
... try :
... action()
... 例外ignore_spec:
... pass
...
> >> 試行(do_something,ignore_spec=(NameError, TypeError))
>>> 試行(do_something,ignore_spec=TypeError)
トレースバック (最新の呼び出し最後):
...
NameError: グローバル名 'blob ' は定義されていません
悪い点 (前の段落で述べた)
これは明白です。欠点は、例外パラメーターのエラーは通常、例外がトリガーされた後でしか気づかないことですが、例外を使用して一般的ではないものをキャプチャする場合には遅すぎることです。特定のテスト ケースを作成しない限り、イベント (例: 書き込み用にファイルを開くことができなかったなど)そして、独自のエラー例外をスローします - これは、NameError が通常意味するものです。
... a, b = do_something(). .. ValueError を除く: # おっと - 誰かが入力できません... print("おっと")... else:... print("OK!") # do_something がトリプルを返すまでは 'ok' です...OK!迷惑 (前の段落で述べた)>>> try:... TypeError = ZeroDivisionError # なぜこれを行うのでしょうか...? !... 1 / 0... ただし、TypeError:... print("Caught!")... else:。 .. print("ok"). ..Caught! 例外パラメータは名前で検索されるだけでなく、他の式も次のように機能します:>>> try:... 1 / 0... eval( ''.join('ゼロ除算エラー'.split())):... print("キャッチ!")... else:.. . print("ok"). ..Caught! 例外パラメータは実行時に決定できるだけでなく、ライフサイクル中に例外情報を使用することもできます。 以下は、スローされた例外をキャッチするためのより複雑な方法です。 - しかし、それはそれだけです: >>> import sys>>> def current_exc_type():... Return sys.exc_info()[0]...>>> try :.. . blob... current_exc_type() を除く:... print ("わかりました!")...わかりました!明らかにこれが私たちが本当に探しているものですこの(バイト)コードは、例外処理でどのように動作するかを確認するために、例外の例で dis.dis() を実行しました。ここでの分解は Python 2.7 で行われます - Python 3.3 では異なるバイトコードが生成されますが、これは基本的に似ています): >>> import dis>>> def x():... try: ... pass... Blobbity を除く:... print("bad")
... else:
... print("good")
...
>>> dis.dis(x) # doctest: +NORMALIZE_WHITESPACE
2 0 SETUP_EXCEPT 4 7)
3 3pop_block
4jump_forward 22(29)
4>> 7dup_top
8load_global 0(blobbity)
11比較10(例外マッチ)
14 pop_jump_if_false 28°17 pop_top
18pop_top
19pop_top
23print_item
24print_newline
25jump_forward 6(to 34)
>> 28end_finalaly
32 PRINT_ITEM
33 PRINT_NEWLINE
>> LOAD_CONST 0 (なし)
37 RETURN_VALUE
この显示我原来预期的问题(問題)。 常用処理「見る起来」は完全に Python 内部機械制で実行されています。 これには、その後の異常な「捕捉」フレーズに関する知識はまったく必要なく、異常が発生しない場合は完全に取得されます。SETUP_EXCEPT は何も発生しませんが、異常が発生した場合のみ、この被评估、その後第二、以此类推。 全てが遅延しており、全てが正しく行われているように、すべての計画を解決する観点から考えられています。
总结
虽然このような動き态の异常パラメータ让我大吃一惊、しかしこれには非常に多くの興味深いアプリケーションが含まれています。 もちろん、現在使用されている多くのまたは承認は主意であり、Python の特性のサポートを完全に承認することはできません。関数、方法、すべての効果領域)、ただしすべてではありませんが、すべてがこのように活発です。 虽然(我认である)那将は十分素晴らしい、表式は実装用に禁止されています - 以下はPython语法错误:
@(lambda fn: fn)
def x():
pass
これです尝试動态异常パラメータ通過给定型传递给第一异常的例子、静静的忍受重复的异常:
>>> class Pushover(object):
... exc_spec = set()
.. .
... def tirming(self、action):
... try:
... return action()
... tuple(self.exc_spec):
...pass
... BaseException を除く e:
... self.exc_spec.add(e.__class__)
... raise
...
>>> Pushover = Pushover()
>>>
>>> for _ in range(4):
... try:
... Pushover.attempt(lambda: 1 / 0)
... 例外:
... print (" Boo")
... else:
... print ("やった!")
Boo
やった!
やった!
やった!