5

次の例はやや意外です。

>>> class Foo:
        def blah(self):
            pass


>>> f = Foo()
>>> def bar(self):
        pass

>>> Foo.bar = bar
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x02D18FB0>>

バインドされたメソッドは、特定の各インスタンスに関連付けられ、構築時に配置されることを期待していました。バインドされたメソッドはインスタンスごとに異なる必要があることは論理的であるように思われます。これにより、基になる関数に渡すインスタンスがわかります。実際、次のようになります。

>>> g = Foo()
>>> g.blah is f.blah
False

しかし、関数をクラス属性に割り当てると、それまでにすでに作成されていたインスタンスに関数が配置されるとは思わないため、プロセスの理解には明らかに欠陥があります。

だから、私の質問は2つあります-

  1. 関数をクラスに割り当てると、インスタンスにさかのぼって適用されるのはなぜですか?これを実現する実際のルックアップルールとプロセスは何ですか?
  2. これは言語によって保証されているものですか、それともたまたま起こっていることですか?
4

2 に答える 2

11

あなたはあなたの心を吹き飛ばしたい、これを試してみてください:

f.blah is f.blah

そうです、インスタンスメソッドラッパーは、アクセスするたびに異なります。

実際、インスタンスメソッドは記述子です。言い換えれば、f.blah実際には:

Foo.blah.__get__(f, type(f))

メソッドは実際にはインスタンスに保存されません。それらはクラスに格納され、メソッドラッパーがその場で生成されてメソッドをインスタンスにバインドします。

于 2012-06-16T03:47:36.027 に答える
9

インスタンスにはメソッドが「含まれていません」。ルックアッププロセスは、にアクセスしたときに動的に行われますfoo.bar。インスタンスにその名前の属性があるかどうかを確認します。そうではないので、クラスを調べて、その時点でクラスが持っている属性を見つけます。メソッドはこの点で特別ではないことに注意してください。Foo.bar = 2;を設定した場合も同じ効果が得られます。その後、foo.bar2に評価されます。

言語によって保証されるのは、属性ルックアップがこの方法で進行することです。最初にインスタンス、次に属性がインスタンスに見つからない場合はクラスです。(ルックアップルールは、演算子のオーバーロードなどを介して暗黙的に呼び出される特別なメソッドでは異なります。)

インスタンスに属性を直接割り当てた場合にのみ、クラス属性がマスクされます。

>>> foo = Foo()
>>> foo.bar
Traceback (most recent call last):
  File "<pyshell#79>", line 1, in <module>
    foo.bar
AttributeError: 'Foo' object has no attribute 'bar'
>>> foo.bar = 2
>>> Foo.bar = 88
>>> foo.bar
2

上記のすべては、バインドされた/バインドされていないメソッドとは別の問題です。Pythonのクラス機構は記述子プロトコルを使用するため、にアクセスするfoo.barと、新しいバインドされたメソッドインスタンスがその場で作成されます。そのため、さまざまなオブジェクトにさまざまなバインドされたメソッドインスタンスが表示されます。ただし、基本的に、これらのバインドされたメソッドは、クラスで記述したメソッドによって定義されたものと同じコードオブジェクトに依存していることに注意してください。

>>> foo = Foo()
>>> foo2 = Foo()
>>> foo.blah.im_func.__code__ is foo2.blah.im_func.__code__
True
于 2012-06-16T03:48:32.063 に答える