まず第一に、これはあなたがwith something
を行うときに何が起こるか、それはタイプに特別な方法を調べているだけcontextlib
ではありません。また、他の特別なメソッドでも同じことが起こることに注意してください。 a + b
の結果はtype(a).__add__(a, b)
になります。
しかし、どうしてですか?これはよくpython-devとpython-ideasのメーリングリストで起きる質問です。私が「しばしば」と言ったとき、私は「非常に」という意味です。
最後のものはMissing Core Feature: + - */| & do not call getattrとEliminating 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__()
を "古典的な"方法で見ても遅すぎるかもしれないので、特別な方法のルックアップを短絡すると、パフォーマンスが向上します。
ご回答ありがとうございます。それでも、なぜ 'a .__ add __(b)'の代わりに 'type(a).__ add __(a、b)'を使うのだろうと思います。彼らは同じではありませんか? – Achimnol
@Achimnol:あなたは何かを 'a____ add__'に割り当てると同じではありません。 – user2357112
@Achimnol:私はその質問にも答えましたが、あまりにも漠然としているかどうか教えてください。 –