10
>>> class A(object): pass
>>> def func(cls): pass
>>> A.func = func
>>> A.func
<unbound method A.func>

この割り当てはどのようにメソッドを作成しますか? 割り当てがクラスに対して次のことを行うのは直感的ではないようです。

  • 関数をバインドされていないインスタンス メソッドに変換する
  • ラップされた関数classmethod()をクラス メソッドに変換します (実際、これは非常に直感的です)。
  • ラップさstaticmethod()れた関数を関数に変換する

最初のものには が必要でinstancemethod()、最後のものにはラッパー関数がまったくないはずです。これらがブロック内で使用されることは理解していますclassが、なぜブロック外に適用する必要があるのでしょうか?

しかし、もっと重要なことは、クラスへの関数の割り当てはどのように機能するのでしょうか? これら3つのことを解決する魔法は何ですか?

これでさらに混乱します:

>>> A.func
<unbound method A.func>
>>> A.__dict__['func']
<function func at 0x...>

しかし、属性を取得するとき、これは記述子と関係があると思います。ここでの属性の設定とはあまり関係がないと思います。

4

5 に答える 5

4

記述子は、通常の関数をインスタンスまたはクラスから取得するときにバインドされたメソッドまたはバインドされていないメソッドに変換する魔法の1です。classmethodおよびデコレーターは他のstaticmethodバインディング戦略を実装し、staticmethod実際には生の関数を返すだけです。これは、非関数callableオブジェクトから得られるのと同じ動作です。

詳細については「ユーザー定義メソッド」</a>を参照してください。ただし、次の点に注意してください。

また、この変換はユーザー定義関数に対してのみ発生することに注意してください。他の呼び出し可能オブジェクト (およびすべての呼び出し可能でないオブジェクト) は、変換なしで取得されます。

したがって、独自の呼び出し可能なオブジェクトに対してこの変換が必要な場合は、それを関数でラップするだけで済みますが、記述子を記述して独自のバインド戦略を実装することもできます。

これは動作中のstaticmethodデコレーターで、アクセスされると基になる関数を返します。

>>> @staticmethod
... def f(): pass
>>> class A(object): pass
>>> A.f = f
>>> A.f
<function f at 0x100479398>
>>> f
<staticmethod object at 0x100492750>

メソッドを持つ通常のオブジェクトは__call__変換されません。

>>> class C(object):
...         def __call__(self): pass
>>> c = C() 
>>> A.c = c 
>>> A.c
<__main__.C object at 0x10048b890>
>>> c
<__main__.C object at 0x10048b890>

1特定の関数はfunc_descr_getObjects /funcobject.cにあります。

于 2010-02-21T23:48:28.767 に答える
4

これは記述子プロトコルと関係があることは間違いありません。記述子は、レシーバー オブジェクトをメソッドの最初のパラメーターとして渡す方法を Python で実装する方法です。ここから Python 属性ルックアップの詳細を読むことができます。以下は、少し下のレベルで、A.func = func; を実行したときに何が起こっているかを示しています。A.func:

# A.func = func
A.__dict__['func'] = func # This just sets the attribute
# A.func
#   The __getattribute__ method of a type object calls the __get__ method with
#   None as the first parameter and the type as the second.
A.__dict__['func'].__get__(None, A) # The __get__ method of a function object
                                    # returns an unbound method object if the
                                    # first parameter is None.
a = A()
# a.func()
#   The __getattribute__ method of object finds an attribute on the type object
#   and calls the __get__ method of it with the instance as its first parameter.
a.__class__.__dict__['func'].__get__(a, a.__class__)
#   This returns a bound method object that is actually just a proxy for
#   inserting the object as the first parameter to the function call.

したがって、クラス属性に割り当てるのではなく、クラスまたはインスタンスで関数を検索してメソッドに変換します。

classmethodstaticmethodはわずかに異なる記述子であり、型オブジェクトにバインドされたバインドされたメソッド オブジェクトを返す classmethod と、元の関数を返すだけの staticmethod です。

于 2010-02-22T01:23:20.877 に答える
3

考慮しなければならないことは、Python ではすべてが object であるということです。何が起こっているのかを理解しやすくすることを確立することによって。関数 がある場合は、 を実行してを呼び出すdef foo(bar): print barことができ、もちろん.spam = foospam(1)1

Python のオブジェクトは、インスタンス属性を、__dict__他のオブジェクトへの「ポインタ」で呼び出されるディクショナリに保持します。Python の関数もオブジェクトであるため、それらを単純な変数として割り当てて操作したり、他の関数に渡したりすることができます。オブジェクト指向の Python の実装では、これを利用して、メソッドを属性として扱い、メソッドを属性として扱います__dict__。オブジェクト。

インスタンス メソッドの最初のパラメータは、常にインスタンス オブジェクト自体であり、通常は と呼ばれますself(ただし、これはthisまたはと呼ばれることもありますbanana)。メソッドが で直接呼び出されると、classどのインスタンスにもバインドされないため、インスタンス オブジェクトを最初のパラメータ ( A.func(A())) として指定する必要があります。バインドされた関数( )を呼び出す場合A().func()、メソッドの最初のパラメーターselfは暗黙的ですが、背後では Python はバインドされていない関数を直接呼び出してインスタンス オブジェクトを最初のパラメーターとして渡すのとまったく同じことを行います。

これが理解されていれば、A.func = func(カーテンの後ろで行われているA.__dict__["func"] = func)代入によってバインドされていないメソッドが残るという事実は驚くべきことではありません。

あなたの例ではclsdef func(cls): pass実際に渡されるのはselftype の instance() ですAclassmethodまたはデコレータを適用すると、関数/メソッドの呼び出し中に取得された最初の引数を取得し、関数を呼び出す前にそれを別のものに変換するだけです。staticmethod

classmethod最初の引数を取り、classインスタンスのオブジェクトを取得し、それを最初の引数として関数呼び出しに渡しますがstaticmethod、最初のパラメーターを単に破棄し、それなしで関数を呼び出します。

于 2010-02-21T22:42:53.220 に答える
0

Gabriel Hurleyの回答に対するMTsoulのコメントに関連して:

異なるのはfunc、メソッドがあり、「呼び出し可能」になっていることです。つまり、演算子をそれに__call__()適用できます。Python ドキュメント()を確認してください(そのページで を検索してください)。__call__

于 2010-02-21T22:39:41.050 に答える
0

ポイント 1:func定義した関数は、Python でFirst-Class オブジェクトとして存在します。

ポイント 2: Python のクラスは、その属性を__dict__.

では、Python でクラス属性の値として関数を渡すとどうなるでしょうか? その関数はクラスに格納され、__dict__割り当てた属性名を呼び出すことによってアクセスされるそのクラスのメソッドになります。

于 2010-02-21T22:30:22.533 に答える