7

Python 2.4 で動作する "with" ステートメントのドロップイン置換をコーディングする方法を提案できますか?

これはハックですが、プロジェクトをよりうまく Python 2.4 に移植することができます。

編集: 無関係なメタクラス スケッチを削除

4

5 に答える 5

7

try-finally を使用してください。

確かに、これは頭の体操としては良いかもしれませんが、気になるコードで実際に行うと、見苦しく保守が難しいコードになってしまいます。

于 2009-10-10T10:47:10.890 に答える
4

これを行うためにデコレータを (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_... コードが実行されます)。デコレーターが関数宣言を即時呼び出しに変更することに注意してください。

于 2009-10-10T10:11:16.290 に答える
1

ブロックを取得するためだけに 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

于 2010-08-19T17:43:48.510 に答える
1

これはどう?

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.

于 2009-10-10T19:46:39.323 に答える
1

エラー中とエラー中の両方でコンテキストマネージャーを終了する必要があるため、メタクラスを使用して一般的なユースケースを実行することは不可能だと思います。実際にはまったく不可能です。そのためには、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)
于 2009-10-10T10:13:20.450 に答える