Python 2.4 で動作する "with" ステートメントのドロップイン置換をコーディングする方法を提案できますか?
これはハックですが、プロジェクトをよりうまく Python 2.4 に移植することができます。
編集: 無関係なメタクラス スケッチを削除
Python 2.4 で動作する "with" ステートメントのドロップイン置換をコーディングする方法を提案できますか?
これはハックですが、プロジェクトをよりうまく Python 2.4 に移植することができます。
編集: 無関係なメタクラス スケッチを削除
try-finally を使用してください。
確かに、これは頭の体操としては良いかもしれませんが、気になるコードで実際に行うと、見苦しく保守が難しいコードになってしまいます。
これを行うためにデコレータを (ab) 使用できると思います。以下の作品、例えば:
def execute_with_context_manager(man):
def decorator(f):
target = man.__enter__()
exc = True
try:
try:
f(target)
except:
exc = False
if not man.__exit__(*sys.exc_info()):
raise
finally:
if exc:
man.__exit__(None, None, None)
return None
return decorator
@execute_with_context_manager(open("/etc/motd"))
def inside(motd_file):
for line in motd_file:
print line,
(まあ、Python 2.4 のファイル オブジェクトには __enter__ メソッドと __exit__ メソッドがありませんが、それ以外の場合は機能します)
アイデアは、次の行で を置き換えることです。
with bar() as foo:
do_something_with(foo)
do_something_else_with(foo)
# etc...
装飾された関数「宣言」を次のように使用します。
@execute_with_context_manager( bar() )
def dummyname( foo ):
do_something_with(foo)
do_something_else_with(foo)
# etc...
しかし、同じ動作を取得します (do_something_... コードが実行されます)。デコレーターが関数宣言を即時呼び出しに変更することに注意してください。
ブロックを取得するためだけに def を使用し、すぐに実行されるデコレーターを使用しても問題ない場合は、関数シグネチャを使用して、名前付きのケースにより自然なものを取得できます。
システムのインポート def with (func): デフ装飾(本体=機能): contexts = body.func_defaults 試す: exc = なし、なし、なし 試す: コンテキスト内のコンテキスト: context.__enter__() 体() を除外する: exc = sys.exc_info() 高める 最後に: 逆のコンテキスト(コンテキスト): context.__exit__(*exc) 装飾された() クラスコンテキスト(オブジェクト): def __enter__(自己): print "Enter %s" % self def __exit__(self, *args): print "Exit %s(%s)" % (self, args) x = コンテキスト() @と デフォルト _(それ = x): print "Body %s" % it @と デフォルト _(それ = x): print "%s の前の本文" % it 「なし」を上げる print "Body after %s" % it
これはどう?
def improvize_context_manager(*args, **kwargs):
assert (len(args) + len(kwargs)) == 1
if args:
context_manager = args[0]
as_ = None
else: # It's in kwargs
(as_, context_manager) = kwargs.items()[0]
def decorator(f):
exit_ = context_manager.__exit__ # Not calling it yet
enter_ = context_manager.__enter__()
exc = True
try:
try:
if as_:
f(*{as_: enter_})
else:
f()
except:
exc = False
if not exit_(*sys.exc_info()):
raise
finally:
if exc:
exit_(None, None, None)
return None
return decorator
使用法:
@improvize_context_manager(lock)
def null():
do(stuff)
なしのwith
キーワードに相当しas
ます。
または:
@improvize_context_manager(my_lock=lock)
def null(my_lock):
do(stuff_with, my_lock)
これは、with
キーワードとas
.
エラー中とエラー中の両方でコンテキストマネージャーを終了する必要があるため、メタクラスを使用して一般的なユースケースを実行することは不可能だと思います。実際にはまったく不可能です。そのためには、try/finally ブロックが必要になります。
しかし、あなたのケースでは何か他のことをすることが可能かもしれません。それは、コンテキストマネージャーを何に使用するかによって異なります。
を使用__del__
すると、リソースの割り当てを解除するなどの場合に役立ちますが、呼び出されるかどうかを確認できないため、プログラムの終了時に解放されるリソースを解放する必要がある場合にのみ使用できます。メソッドで例外を処理している場合も機能しません__exit__
。
最もクリーンな方法は、一種のコンテキスト管理呼び出しでコンテキスト管理全体をラップし、コード ブロックをメソッドに抽出することだと思います。このようなもの (テストされていないコードですが、ほとんどが PEP 343 から盗用されています):
def call_as_context_manager(mgr, function):
exit = mgr.__exit__
value = mgr.__enter__()
exc = True
try:
try:
function(value)
except:
exc = False
if not exit(*sys.exc_info()):
raise
finally:
if exc:
exit(None, None, None)