10

Python 3.4 は、stdout を一時的にリダイレクトするためのこのきちんとしたツールを提供します。

# From https://docs.python.org/3.4/library/contextlib.html#contextlib.redirect_stdout
with redirect_stdout(sys.stderr):
    help(pow)

コードはそれほど複雑ではありませんが、何度も何度も書きたくありません。

class redirect_stdout:
    def __init__(self, new_target):
        self._new_target = new_target
        # We use a list of old targets to make this CM re-entrant
        self._old_targets = []

    def __enter__(self):
        self._old_targets.append(sys.stdout)
        sys.stdout = self._new_target
        return self._new_target

    def __exit__(self, exctype, excinst, exctb):
        sys.stdout = self._old_targets.pop()

withステートメントを使用して変数の値を一時的に変更する一般的な方法があるかどうか疑問に思っています。の他の 2 つの使用例syssys.stderr、 とsys.excepthookです。

完璧な世界では、次のようなものが機能します。

foo = 10
with 20 as foo:
    print(foo) # 20
print (foo) # 10

私はそれを機能させることができるとは思っていませんが、おそらく次のようなことが可能です:

foo = 10
with temporary_set('foo', 20):
    print(foo) # 20
print (foo) # 10

を根回しすることでこれを機能させることができますが、globals()誰もが使用することを選択するものではありません.

更新: 私の「foo = 10」の例は、私がやろうとしていることを明確にしたと思いますが、実際の使用例を伝えていません。ここに2つあります:

  1. redirect_stdout と同じように stderr をリダイレクトします。
  2. sys.excepthook を一時的に変更します。私はインタラクティブに多くの開発を行っており、excepthook に何かを追加するとき (たとえば、logging モジュールを使用して例外をログに記録するために、元の関数を独自の関数にラップすることによって)、ある時点でそれを削除したいと考えています。そうすれば、関数ラッピング自体のコピーがますます増えなくなります。この質問は、密接に関連する問題に直面しています。
4

5 に答える 5

9

私はこの質問がちょっと古いことを知っていますが、同じ問題に遭遇したので、ここに私の解決策があります:

class test_context_manager():
    def __init__(self, old_object, new_object):
        self.new = new_object
        self.old = old_object
        self.old_code = eval(old_object)
    def __enter__(self):
        globals()[self.old] = self.new
    def __exit__(self, type, value, traceback):
        globals()[self.old] = self.old_code

グローバル変数を多用するのできれいではありませんが、うまくいくようです。

例えば:

x = 5
print(x)
with test_context_manager("x", 7):
    print(x)

print(x)

結果:

5
7
5

または関数で:

def func1():
    print("hi")

def func2():
    print("bye")

x = 5
func1()
with test_context_manager("func1", func2):
    func1()

func1()

結果:

hi
bye
hi
于 2015-09-24T13:59:40.733 に答える
2

私は通常、このカスタムattr_asコンテキスト マネージャーを使用します。

from contextlib import contextmanager

@contextmanager
def attr_as(obj, field:str, value) -> None:
    old_value = getattr(obj, field)
    setattr(obj, field, value)
    yield
    setattr(obj, field, old_value)

setattrその後、 in に使用するのとまったく同じ引数のセットで使用できますattr_as

class Foo:
    def __init__(self):
        self.x = 1

foo = Foo()
with attr_as(foo, 'x', 2):
    print(foo.x)

bar = 3
with attr_as(sys.modules[__name__], 'bar', 4):
    print(bar) 

値だけでなく、属性の存在/非存在を保持する必要がある場合は、数行追加するだけで可能です。

from contextlib import contextmanager

@contextmanager
def attr_as(obj, field:str, value) -> None:
    old_exists = hasattr(obj, field)
    if old_exists:
        old_value = getattr(obj, field)
    setattr(obj, field, value)
    yield
    if old_exists:
        setattr(obj, field, old_value)
    else:
        delattr(obj, field)
于 2021-03-02T19:30:21.560 に答える