時々私はそのようないくつかの連続したコマンドを実行したい状況に自分自身を見つけます:
try:
foo(a, b)
except Exception, e:
baz(e)
try:
bar(c, d)
except Exception, e:
baz(e)
...
この同じパターンは、例外を単に無視する必要がある場合にも発生します。
これは冗長に感じられ、過度の構文により、コードを読み取る際に従うのが驚くほど難しくなります。
Cでは、この種の問題はマクロで簡単に解決できただろうが、残念ながら、これは単純なpythonでは実行できません。
質問:このパターンに遭遇したときに、コードフットプリントを減らしてコードを読みやすくするにはどうすればよいですか?
python 2.5以上の場合、 with
ステートメント を使用できます。
from __future__ import with_statement
import contextlib
@contextlib.contextmanager
def handler():
try:
yield
except Exception, e:
baz(e)
あなたの例は次のようになります:
with handler():
foo(a, b)
with handler():
bar(c, d)
これが常にある場合、always特定の関数が例外を発生させたときの動作は、デコレータを使用できます。
def handle_exception(handler):
def decorate(func):
def call_function(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception, e:
handler(e)
return call_function
return decorate
def baz(e):
print(e)
@handle_exception(baz)
def foo(a, b):
return a + b
@handle_exception(baz)
def bar(c, d):
return c.index(d)
使用法:
>>> foo(1, '2')
unsupported operand type(s) for +: 'int' and 'str'
>>> bar('steve', 'cheese')
substring not found
単純な1行のコマンドの場合は、lambda
sで囲むことができます。
for cmd in [
(lambda: foo (a, b)),
(lambda: bar (c, d)),
]:
try:
cmd ()
except StandardError, e:
baz (e)
その全体を関数にラップできるので、次のようになります。
ignore_errors (baz, [
(lambda: foo (a, b)),
(lambda: bar (c, d)),
])
このようなものを試すことができます。これは漠然とCマクロに似ています。
class TryOrBaz( object ):
def __init__( self, that ):
self.that= that
def __call__( self, *args ):
try:
return self.that( *args )
except Exception, e:
baz( e )
TryOrBaz( foo )( a, b )
TryOrBaz( bar )( c, d )
私が見つけた最良のアプローチは、そのような関数を定義することです:
def handle_exception(function, reaction, *args, **kwargs):
try:
result = function(*args, **kwargs)
except Exception, e:
result = reaction(e)
return result
しかし、それは実際には正しく感じられません。
handle_exception(foo, baz, a, b)
handle_exception(bar, baz, c, d)