3

リフレクションについて質問する際に、私は次のように尋ねました。

いい答え。しかし、言うことと を言うことには違いがmyobject.foo()ありx = getattr(myobject, "foo"); x();ます。たとえそれが化粧品であっても。最初に foo() が静的にコンパイルされます。2 番目の例では、文字列はさまざまな方法で作成できます。– ジョー 1時間前

どちらが答えを得ました:

ええと、ジャガイモ/ソラナム・ツベロスム... pythonでは、nietherは静的にコンパイルされるため、多かれ少なかれ同等です。– SWeko 1時間前

Python オブジェクトのメンバーがディクショナリに格納され、すべてが動的であることはわかっていますが、次のコードが与えられていると仮定しました。

class Thing():
  def m(self):
    pass

t = Thing()

次のコードは、.pyc が生成されるときに静的にコンパイルされます。

t.m()

つまり、コンパイラは のアドレスを知っているm()ので、実行時にポイント バインディングはありません。それまたはランタイムは、後続のルックアップをキャッシュします。

これには常に辞書のヒットが含まれますが、

meth = getattr(t, "m")
meth()

すべての呼び出しは辞書で文字列検索として扱われますか? それとも、2 つの例は実際には同一ですか?

4

5 に答える 5

7

これらは完全に同一ではありませんが、逆アセンブラで表示できるように、両方とも辞書ルックアップdis.disです。

LOAD_ATTR特に、 with命令は、属性を名前で動的に検索することに注意してください。ドキュメントによると、「TOS[スタックの最上位]をgetattr(TOS, co_names[namei])」に置き換えます。

>>> from dis import dis
>>> dis(lambda: t.m())
  1           0 LOAD_GLOBAL              0 (t)
              3 LOAD_ATTR                1 (m)
              6 CALL_FUNCTION            0
              9 RETURN_VALUE        
>>> dis(lambda: getattr(t, 'm')())
  1           0 LOAD_GLOBAL              0 (getattr)
              3 LOAD_GLOBAL              1 (t)
              6 LOAD_CONST               0 ('m')
              9 CALL_FUNCTION            2
             12 CALL_FUNCTION            0
             15 RETURN_VALUE        
于 2011-01-20T12:41:14.690 に答える
3

すべての呼び出しは、辞書検索として扱われます (まあ、内部的に python は何らかの最適化を行っているかもしれませんが、それがどのように機能するかについては、それらが辞書検索であると想定できます)。

Python には静的コンパイルはありません。これを行うことさえ可能です:

t = Thing()
t.m = lambda : 1
t.m()
于 2011-01-20T12:35:27.837 に答える
2

Python 言語について質問しているのか、CPython などの特定の実装について質問しているのかによって異なります。

言語自体は、この 2 つが同一であるとは言っていないため、何らかの方法で属性への直接アクセスを最適化できる可能性があります。ただし、Python の動的な性質により、これを一貫して行うことは困難です。そのため、CPython では、この 2 つは事実上同一です。

そうは言っても、ダイレクトは、2 回の辞書検索ではなく 1 回の辞書検索を伴うためt.m()、使用する場合の約 2 倍の速度になる可能性があります。つまり、グローバル名自体を辞書で検索する必要があります。getattr()getattr()getattr()

于 2011-01-20T13:30:21.593 に答える
1

これgetattrを使用すると、名前が有効な識別子ではない属性にアクセスできますが、辞書を使用する代わりに、そのような属性を使用するユースケースがあるかどうかはわかりません。

>>> setattr(t, '3', lambda : 4)
>>> t.3()
  File "<stdin>", line 1
    t.3()
      ^
SyntaxError: invalid syntax
>>> getattr(t, '3')()
4
于 2011-01-20T14:08:01.650 に答える
1

実行時に変更できるのはクラスだけではありません ( HS の例のように)。ただし、キーワードでさえclass実行時に「実行」されます。

クラス定義は実行可能なステートメントです。継承リストがある場合は、まずそれを評価します。継承リストの各項目は、サブクラス化が可能なクラス オブジェクトまたはクラス タイプに評価される必要があります。次に、クラスのスイートは、新しく作成されたローカル名前空間と元のグローバル名前空間を使用して、新しい実行フレーム (セクション 命名とバインディングを参照) で実行されます。(通常、スイートには関数定義のみが含まれます。) クラスのスイートの実行が終了すると、その実行フレームは破棄されますが、そのローカル名前空間は保存されます。[4] 次に、基本クラスの継承リストと属性ディクショナリの保存されたローカル名前空間を使用して、クラス オブジェクトが作成されます。クラス名は、元のローカル名前空間でこのクラス オブジェクトにバインドされます。

( Python 言語リファレンス 7.7: クラス定義)

つまり、起動時に、インタープリターはclassブロック内のコードを実行し、結果のコンテキストを保持します。コンパイル時には、クラスがどのようになるかを知る方法はありません。

于 2011-01-20T12:44:44.080 に答える