71

この回答のコメント スレッドに関する議論のため、この質問をしています。私は頭を丸めるまでの道の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>

f1 で記述されているバインドされたメソッドバインドされていないメソッド、および関数オブジェクトの違いは何ですか? これらの 3 つのオブジェクトをどのように呼びますか? 彼らはどのようにお互いに変身することができますか?このことに関するドキュメントは、理解するのが非常に困難です。

4

6 に答える 6

82

関数は、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>

関数と非バインドメソッドの違いはごくわずかであるため、Python3はその違いを取り除きます。Python 3では、クラスインスタンスの関数にアクセスすると、関数自体が提供されます。

>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True

Python2とPython3の両方で、これら3つは同等です。

f1(C())
C.f1(C())
C().f1()

関数をインスタンスにバインドすると、最初のパラメーター(通常はself)をインスタンスに固定する効果があります。したがって、バインドされたメソッドC().f1は、次のいずれかと同等です。

(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())
于 2012-08-14T10:09:53.380 に答える
9

かなりわかりにくい

これは非常に難しいトピックであり、記述子に関係しています。

関数から始めましょう。ここですべてが明らかです - あなたはそれを呼び出すだけで、提供されたすべての引数は実行中に渡されます:

>>> f = A.__dict__['f1']
>>> f(1)
1

TypeErrorパラメータの数に問題がある場合は、Regularが発生します。

>>> 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と、そのインスタンスを最初の引数として original を実行します。

>>> 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まだバインドされていますが、クラスにのみバインドされていることを意味します。

于 2012-08-14T10:25:37.870 に答える
4

関数オブジェクトは、関数定義によって作成される呼び出し可能なオブジェクトです。バインドされたメソッドとバインドされていないメソッドはどちらも、ドット二項演算子によって呼び出される Descriptor によって作成される呼び出し可能なオブジェクトです。

バインドされたメソッド オブジェクトとバインドされていないメソッド オブジェクトにはim_func、クラスで定義された関数オブジェクトim_class、クラス、およびim_selfクラス インスタンスの 3 つの主要なプロパティがあります。バインドされていないメソッドの場合、im_selfis None.

バインドされたメソッドが呼び出されると、最初のパラメーターとして呼び出しim_funcim_selfその後に呼び出しパラメーターが続きます。バインドされていないメソッドは、呼び出しパラメーターだけで基になる関数を呼び出します。

Python 3 以降、バインドされていないメソッドはありません。Class.methodメソッドへの直接参照を返します。

于 2012-08-14T10:49:30.363 に答える
1

今日見た興味深いことの 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")
于 2014-11-15T06:45:27.837 に答える