this answer のコメントスレッドに関する議論のため、この質問をしています。私は90%の方法で頭を動かしています。
In [1]: class A(object): # class named 'A'
...: def f1(self): pass
...:
In [2]: a = A() # an instance
f1
は3つの異なる形式で存在します。
In [3]: a.f1 # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1 # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1'] # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1'] # a function
Out[6]: <function __main__.f1>
boundメソッド、unboundメソッドとfunctionオブジェクト、それらはすべてf1?これら3つのオブジェクトをどのように呼び出すのですか?それらはどのように互いに変換できますか? documentation このことについては理解するのが非常に難しいです。
関数は、def
ステートメントまたはlambda
によって作成されます。 Python 2)の下で、関数がclass
ステートメントの本体内に表示される(またはtype
クラス構築呼び出しに渡される)と、 非バインドメソッド。(Python 3には非バインドメソッドがありません。以下を参照してください。)クラスインスタンスで関数にアクセスすると、バインドメソッドに変換されます。インスタンスに最初のself
パラメーターとしてインスタンスを自動的に提供します。
_def f1(self):
pass
_
ここで_f1
_は関数です。
_class C(object):
f1 = f1
_
現在、_C.f1
_はバインドされていないメソッドです。
_>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True
_
type
クラスコンストラクターも使用できます。
_>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>
_
_f1
_を非バインドメソッドに手動で変換できます。
_>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>
_
非バインドメソッドは、クラスインスタンスのアクセスによってバインドされます。
_>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>
_
アクセスは、記述子プロトコルを介した呼び出しに変換されます。
_>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
_
これらを組み合わせる:
_>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>
_
または直接:
_>>> types.MethodType(f1, C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>
_
関数と非バインドメソッドの主な違いは、メソッドがバインドされているクラスを知っていることです。非バインドメソッドの呼び出しまたはバインドには、そのクラスタイプのインスタンスが必要です。
_>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>
_
関数と非バインドメソッドの違いはごくわずかであるため、Python 3は区別を取り除きます; Python 3はクラスインスタンスの関数にアクセスします関数自体を提供します:
_>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True
_
Python 2とPython 3の場合、これら3つは同等です:
_f1(C())
C.f1(C())
C().f1()
_
関数をインスタンスにバインドすると、その最初のパラメーター(従来はself
と呼ばれる)がインスタンスに固定されます。したがって、バインドメソッドC().f1
は次のいずれかと同等です。
_(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())
_
理解しにくい
まあ、それは非常に難しいトピックであり、記述子に関係しています。
関数から始めましょう。ここではすべてが明確です-あなたはそれを呼び出すだけで、実行中に提供されたすべての引数が渡されます:
_>>> f = A.__dict__['f1']
>>> f(1)
1
_
通常のTypeError
は、パラメーターの数に問題がある場合に発生します。
_>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)
_
さて、メソッド。メソッドは、少しスパイスの効いた関数です。ここで記述子がゲームに登場します。 データモデル で説明されているように、_A.f1
_およびA().f1
はそれぞれA.__dict__['f1'].__get__(None, A)
およびtype(a).__dict__['f1'].__get__(a, type(a))
に変換されます。そして、これらの___get__
_の結果は、生の_f1
_関数とは異なります。これらのオブジェクトは、元の_f1
_のラッパーであり、いくつかの追加ロジックが含まれています。
_unbound method
_の場合、このロジックには、最初の引数がA
のインスタンスであるかどうかのチェックが含まれます。
_>>> f = A.f1
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead)
_
このチェックが成功すると、そのインスタンスを最初の引数として元の_f1
_を実行します:
_>>> f(A())
<__main__.A object at 0x800f238d0>
_
_im_self
_属性はNone
であることに注意してください:
_>>> f.im_self is None
True
_
_bound method
_の場合、このロジックはすぐに元の_f1
_に作成されたA
のインスタンスを提供します(このインスタンスは実際には_im_self
_属性に格納されます)。
_>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>
_
したがって、bound
は、基になる関数が特定のインスタンスにバインドされていることを意味します。 unbound
は、クラスにのみバインドされていることを意味します。
関数オブジェクトは、関数定義によって作成された呼び出し可能なオブジェクトです。バインドされたメソッドとバインドされていないメソッドはどちらも、ドットバイナリ演算子によって呼び出された記述子によって作成された呼び出し可能なオブジェクトです。
バインドおよび非バインドメソッドオブジェクトには、3つの主要なプロパティがあります。im_func
はクラスで定義された関数オブジェクト、im_class
はクラスであり、im_self
はクラスインスタンスです。非バインドメソッドの場合、im_self
はNone
です。
バウンドメソッドが呼び出されると、im_func
with im_self
は、最初のパラメーターとして呼び出しパラメーターの後に続きました。バインドされていないメソッドは、呼び出しパラメーターだけで基になる関数を呼び出します。
今日私が見た興味深いことの1つは、関数をクラスメンバーに割り当てると、そのメソッドが非バインドメソッドになることです。といった:
class Test(object):
@classmethod
def initialize_class(cls):
def print_string(self, str):
print(str)
# Here if I do print(print_string), I see a function
cls.print_proc = print_string
# Here if I do print(cls.print_proc), I see an unbound method; so if I
# get a Test object o, I can call o.print_proc("Hello")
詳細については、 Python 2 および Python のドキュメントを参照してください。
私の解釈は次のとおりです。
クラスFunction
スニペット:
Python 3:
_class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return types.MethodType(self, obj)
_
Python 2:
_class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj, objtype)
_
関数がクラスまたはインスタンスから呼び出されると、その___get__
_が呼び出されてラップされた関数を取得します。
a。 _B.x
_はB.__dict__['x'].__get__(None, B)
と同じです。 Python 3、これは単純な関数を返します。Python 2、これは非バインド関数を返します。
b。 _b.x
_はtype(b).__dict__['x'].__get__(b, type(b)
と同じです。これは、Python 2とPython 3の両方でバインドされたメソッドを返します。つまり、self
が暗黙的に最初の引数として渡されます。
関数、非バインドメソッド、バインドメソッドの違いは何ですか?
画期的な関数とは観点から違いはありません。 Pythonオブジェクト指向機能は、関数ベースの環境に基づいています。
制限されていることは次と等しい:
以下に例を示します。
class C:
#instance method
def m1(self, x):
print(f"Excellent m1 self {self} {x}")
@classmethod
def m2(cls, x):
print(f"Excellent m2 cls {cls} {x}")
@staticmethod
def m3(x):
print(f"Excellent m3 static {x}")
ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)
print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)
出力:
Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
出力は、静的関数m3
が呼び出されることはありませんbound。 C.m2
は、クラスポインターであるC
パラメーターを送信したため、cls
クラスにバインドされます。
ci.m1
およびci.m2
は両方ともバインドされています。 ci.m1
は、インスタンスへのポインタであるself
とci.m2
は、クラスがバインドされていることをインスタンスが知っているためです;)。
メソッドは元々クラスの一部ではないかもしれないことに注意してください。詳細については、 this Alex Martelliからの回答を確認してください。