2016-10-05 5 views
2

withステートメントで呼び出された場合、コンテキストマネージャとしても機能する関数を作成したいと考えています。使用例は次のようになります。これは、標準機能openが使用されている方法と非常に似てコンテキストマネージャを関数として使用する

# Use as function  
set_active_language("en") 

# Use as context manager 
with set_active_language("en"): 
    ... 

active_language = None # global variable to store active language 

class set_active_language(object): 

    def __init__(self, language): 
     global active_language 
     self.previous_language = active_language 
     active_language = language 

    def __enter__(self): 
     pass 

    def __exit__(self, *args): 
     global active_language 
     active_language = self.previous_language 

このコードはスレッドセーフではありませんが、これは問題とは関係ありません。

ここで私が思いついた解決策です。

私はこの解決策について気に入らないのは、クラスコンストラクタが単純な関数であると主張し、その副作用に対してのみ使用されるということです。

これを行うより良い方法はありますか?

このソリューションはテストしていません。

更新:私が機能とコンテキストマネージャを別々のエンティティに分割したくない理由は、命名です。関数とコンテキストマネージャは基本的に同じことを行います。したがって、両方に1つの名前を使用することは合理的です。コンテキストプロセッサの名前を付けることは、別にしておきたい場合には問題になります。それは何でしょうか? active_language?この名前は変数名と衝突する可能性があります。しかし、override_active_languageが動作する可能性があります。

+0

あなたは 'contextlib'から' contextmanager'デコレータを使うことができますが、あなたの関数はジェネレータでなければなりません。 https://docs.python.org/2/library/contextlib.html#contextlib.contextmanager –

+0

'open'は、オブジェクトの参照カウントが0になったときにクリーンアップされる状態(開いているファイルハンドル)を保持します。 'open( 'foo')'(何にも割り当てることなく)は、その副作用のためだけに有用です。だから、質問... 'foo = set_active_language(); del foo'を元の言語に戻したいですか?もしそうなら、コールは 'open'のように状態を運んでいます。 – tdelaney

+0

あなたは非常に疑わしい価値のあるものを設計/実装しようとしています。それについて考える:ユーザーは本当にそれが必要なのか?それがあなたのコードを役に立つものにしていますか?疑わしい。 私はクラス(オブジェクトではありません!)が関数ANDコンテクストマネージャーであると期待していた最も驚くべきことの最後のことです。関数とコンテキストマネージャを別々のエンティティにする。既存のプラクティスに従う:https://github.com/django/django/blob/master/django/utils/translation/__init__py#L170 – Andrey

答えて

2

技術的には、これはできません。しかし、あなたは十分にそれを偽造することができます人々(誰かがそれを思い浮かべなかった)気付かないだろう。

def set_active_language(language): 
    global active_language 
    previous_language = active_language 
    active_language = language 

    class ActiveScope(object): 
     def __enter__(self): 
      pass 

     def __exit__(self, *args): 
      global active_language 
      active_language = previous_language 

    return ActiveScope() 

機能として使用すると、ActiveScopeクラスはわずかに無駄なノーオペレーションです。

+1

関数の外側に 'ActiveScope'を宣言し、' language'を引数として '__init__'をクロージャーで取り込むのではなく、それを渡すことで少し安くすることができます。次に、 'set_active_language'を呼び出すたびにクラスを定義する代わりに、' ActiveScope'をインスタンス化するための費用を支払うだけです。 – chepner

0

うまくいけば誰かが私を間違っていると思うかもしれませんが、答えはではないといいます。:それ以外の方法はありません。また、あなたが選んだ方法のもう一つの短所は、with a, b, c:ステートメントで他のコンテキストマネージャと一緒に使用すると誤動作する可能性があるということです。 CMの意図された副作用は、オブジェクトの構築時に実行され、__enter__メソッドでは実行されません。

withステートメントでコンテキストマネージャーとして初期化されたか、単に関数として呼び出されたかは、クラスコンストラクターの内部から知る必要があります。私が言う限りでは、それを集める方法はありません。inspectモジュールでさえも、それを集める方法はありません。

関連する問題