2015-12-28 9 views
8

contextlib.pyでは、指定されたオブジェクト(cm)への直接メソッド呼び出しの代わりに、タイプオブジェクト(type(cm))を介してExitStackクラスが__enter__()メソッドを呼び出していることがわかります。Python標準のcontextlibで `x .__ enter __()`の代わりに `type(x).__ enter __(x)`を使うのはなぜですか?

私はなぜ、なぜそうではないのだろうか。エラーが発生したとき

例えば、

  • それがより良い例外トレースを与えるのでしょうか?
  • は、モジュール作成者のコーディングスタイルに固有のものですか?
  • パフォーマンス上の利点はありますか?
  • 複雑な型階層を持つアーティファクト/副作用を回避できますか?

答えて

12

まず第一に、これはあなたがwith somethingを行うときに何が起こるか、それはタイプに特別な方法を調べているだけcontextlibではありません。また、他の特別なメソッドでも同じことが起こることに注意してください。 a + bの結果はtype(a).__add__(a, b)になります。

しかし、どうしてですか?これはよくpython-devとpython-ideasのメーリングリストで起きる質問です。私が「しばしば」と言ったとき、私は「非常に」という意味です。

最後のものはMissing Core Feature: + - */| & do not call getattrEliminating special method lookupです。ここで

は、いくつかの興味深いポイントです:

現在の動作は仕様です - 特別なメソッドは、オブジェクトのクラスに スロットとしてではなく、インスタンスの属性として検索されます。これにより、 インタプリタは通常のインスタンス 属性検索プロセスのいくつかの手順をバイパスできます。

(Source)

それは行動がさらに魔法の本よりであることは注目に値します。 クラスを参照しても、暗黙的な特殊メソッド参照 は、メタクラスの__getattr____getattribute__をバイパスします。したがって、 特別なメソッドの検索は、 に起こる通常の検索ではなく、インスタンスの代わりにクラスで開始されます。 の通常の属性アクセスカスタマイズフックに従事していない完全に魔法のルックアップ です。

(Source)

この動作は、リファレンスドキュメントに文書化されている:Special method lookup、こう述べています。

はこの方法で__getattribute__()機械をバイパスで、インタプリタ内のスピード最適化のための重要な範囲を提供します特別なメソッドの処理におけるある程度の柔軟性のためのコスト(インタプリタによって一貫して呼び出されるためには、クラスオブジェクト自体に特別なメソッドを設定する必要があります)。

要するに、のパフォーマンスが主な関心事です。しかし、これを詳しく見てみましょう。

type(obj).__enter__()obj.__enter__()の違いは何ですか?

obj.attrと書くと、type(obj).__getattribute__('attr')が呼び出されます。 __getattribute__()の既定の実装では、インスタンス辞書(obj.__dict__)とクラス名前空間にattrが含まれています。その場合、type(obj).__getattr__('attr')が呼び出されます。

これは簡単な説明であり、いくつかの詳細は省略しましたが、属性ルックアップがどれほど複雑で、どのくらい遅くなるかを知ることができます。 obj.__enter__()を "古典的な"方法で見ても遅すぎるかもしれないので、特別な方法のルックアップを短絡すると、パフォーマンスが向上します。

+0

ご回答ありがとうございます。それでも、なぜ 'a .__ add __(b)'の代わりに 'type(a).__ add __(a、b)'を使うのだろうと思います。彼らは同じではありませんか? – Achimnol

+1

@Achimnol:あなたは何かを 'a____ add__'に割り当てると同じではありません。 – user2357112

+1

@Achimnol:私はその質問にも答えましたが、あまりにも漠然としているかどうか教えてください。 –

関連する問題