4
class C:
  def func(self, a):
    print(a)

c = C()
print(c.__dict__) # {}
c.func = c.func
# c.func is now an instance attribute, which hides instance method
print(c.__dict__) # {'func' : (bound method C.f of (__main.C object at ...))}
# how come it still works as if it's an instance method:
c.func(1) # prints 1

# with a regular function assigned to c.func, this won't happen:
def newfunc(self, a):
  print(a)
c.func = newfunc
c.func(1) # TypeError

呼び出しが最初のパラメーターとしてc.func(1)インスタンスを魔法のように追加する条件は何ですか?クラスでインスタンスメソッドとして定義されているc場合にのみ発生すると思いました。funcただし、が単なるインスタンス属性の場合は機能することがあるようです。func

4

2 に答える 2

3

インスタンスへのバインドは、呼び出すときではなく、アクセスするときに行われます。 c.funcを実行するc.func = c.funcと、右側の属性アクセスはクラスによって処理されC、バインドされたメソッドに評価されます。再割り当てc.funcしてインスタンスに割り当てますが、すでにバインドされたメソッドであるため、動作は変わりません。

newfuncの例では、関数を定義して割り当てただけなので、インスタンスメソッドになりませんでした。インスタンスに直接割り当てるため、クラスはアクセスをインターセプトしてインスタンスメソッドでラップする機会がありません。

バインドされたメソッドはどこにでも割り当てることができ、それを取得したインスタンスにバインドされたままになります。次の例を参照してください。

>>> class C(object):
...     def __init__(self, name):
...         self.name = name
...     def func(self, a):
...         print("Called {0} with {1}".format(self.name, a))
>>> one = C("one")
>>> two = C("two")
>>> one.func(1)
Called one with 1
>>> two.func(1)
Called two with 1
>>> one.func2 = two.func
>>> one.func2(1)
Called two with 1

を割り当てたらone.func2 = two.func、呼び出しは、ではなく、にone.func2バインドされたインスタンスメソッドを呼び出すことに注意してください。これは、にアクセスしたときに、そのインスタンスにバインドされたメソッドを取得したためです。後でどこに割り当てるかは関係ありません。アクセスされたインスタンスにバインドされたままになります。twoonetwo.func

バインドされていないメソッドに直接アクセスして別の場所に割り当てようとすると、メソッドがバインドされなかったため、TypeErrorが発生します。

>>> one.func3 = C.func
>>> one.func3(1)
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    one.func3(1)
TypeError: unbound method func() must be called with C instance as first argument (got int instance instead)

また、メソッドをクラスに追加すると、メソッドをクラスに追加する前にインスタンスが作成された場合でも、インスタンスで呼び出されたときに機能することに注意してください。以前と同じoneオブジェクトを保持して、私は次のことができます。

>>> def newFunc(self, a):
...     print("Newfunc of {0} called with {1}".format(self.name, a))
>>> C.newFunc = newFunc
>>> one.newFunc(1)
Newfunc of one called with 1

質問に簡潔に答えるために、は、メソッドを持つクラスのインスタンスであり、それ自体にはインスタンス属性を持たないifc.funcにバインドされます。(同じ名前のインスタンス属性があった場合、それはクラス1をオーバーライドするため、クラスはメソッドをインスタンスにバインドするためにラッピングを実行できません。)ccfunccfuncc

于 2012-07-31T21:19:06.567 に答える
1

c.func(1)メソッドがオブジェクトにバインドされるcときに、インスタンスを最初のパラメータとして魔法のように追加します。c.funcc

クラスの属性である関数は、そのクラスのインスタンスに自動的にバインドされます。したがって、の直後はc = C()、1つ少ない引数を受け入れ、魔法のように最初の引数になるというc.func点で異なります。これが何も変わらない理由であり、すべてが正常に機能します。C.funccc.func = c.func

クラスのインスタンスに新しいインスタンスメソッドでパッチを適用する場合は、関数をインスタンスにバインドする必要があります。ここにいくつかのオプションがあります:

  • 使用types.MethodType

    import types
    c.func = types.MethodType(newfunc, c)
    
  • 使用functools.partial

    import functools
    c.func = functools.partial(newfunc, c)
    

2番目の方法は動作を模倣しますが、の代わりにtype(c.func)なるため少し異なります。したがって、最初の方法がおそらく望ましいことに注意してください。<type 'functools.partial'><type 'instancemethod'>

于 2012-07-31T21:24:41.760 に答える