5

まず、A2 つのクラス変数と 2 つのインスタンス変数を持つクラスがあります。

In [1]: def fun(x, y): return x + y

In [2]: class A:
   ...:     cvar = 1
   ...:     cfun = fun
   ...:     def __init__(self):
   ...:         self.ivar = 100
   ...:         self.ifun = fun

int 型のクラス変数とインスタンス変数の両方が正常に機能することがわかります。

In [3]: a = A()

In [4]: a.ivar, a.cvar
Out[4]: (100, 1)

ただし、関数型変数を確認すると、状況が変わります。

In [5]: a.ifun, a.cfun
Out[5]: 
(<function __main__.fun>,
 <bound method A.fun of <__main__.A instance at 0x25f90e0>>)

In [6]: a.ifun(1,2)
Out[6]: 3

In [7]: a.cfun(1,2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/home/future/<ipython-input-7-39aa8db2389e> in <module>()
----> 1 a.cfun(1,2)

TypeError: fun() takes exactly 2 arguments (3 given)

Pythonが変換a.cfun(1,2)されA.cfun(a,1,2)てからエラーが発生したことを知っていました。

私の質問は:両方ともcvarクラスcfun変数であるため、なぜpythonはそれらを異なる方法で扱うのですか?

4

1 に答える 1

3

実際、クラスメンバーに割り当てられた関数は関数のままです:

def x():pass

class A:  
    f = x
    e = None
    g = None

print(A.__dict__['f'])
# <function x at 0x10e0a6e60>

インスタンスから取得すると、その場でメソッド オブジェクトに変換されます。

print(A().f)
# <bound method A.x of <__main__.A instance at 0x1101ddea8>>

http://docs.python.org/2/reference/datamodel.html#the-standard-type-hierarchy "ユーザー定義メソッド":

ユーザー定義メソッド オブジェクトは、その属性がユーザー定義関数オブジェクト、バインドされていないユーザー定義メソッド オブジェクト、またはクラス メソッド オブジェクトである場合、クラスの属性を (おそらくそのクラスのインスタンスを介して) 取得するときに作成できます。 .. クラスまたはインスタンスから属性が取得されるたびに、関数オブジェクトから (非バインドまたはバインド) メソッド オブジェクトへの変換が行われることに注意してください。

この変換は、インスタンスではなく、クラスに割り当てられた関数に対してのみ発生します。これは Python 3 で変更されClass.fun、「バインドされていないメソッド」ではなく通常の関数を返すことに注意してください。

なぜこれが必要なのかという質問については、メソッドオブジェクトは本質的に、実行コンテキスト(「自己」)とともに関数を含むクロージャーです。オブジェクトがあり、そのメソッドをどこかでコールバックとして使用しているとします。他の多くの言語では、オブジェクト ポインターとメソッド ポインターの両方を渡すか、クロージャーを手動で作成する必要があります。たとえば、JavaScript では次のようになります。

  myListener = new Listener()
  something.onSomeEvent = myListener.listen // won't work!
  something.onSomeEvent = function() { myListener.listen() } // works

Python は、舞台裏でそれを管理します。

  myListener = Listener()
  something.onSomeEvent = myListener.listen // works

一方で、クラスに「生の」関数または「外部」メソッドを含めることが実用的な場合もあります。

  def __init__(..., dir, ..):
       self.strip = str.lstrip if dir == 'ltr' else str.rstrip
       ...
  def foo(self, arg):
       self.strip(arg)

上記の規則 (class vars => メソッド、instance vars => 関数) は、両方を持つ便利な方法を提供します。

追加する必要はありませんが、Python の他のすべてと同様に、この動作を変更できます。つまり、関数をメソッドに変換せず、そのまま返すクラスを作成できます。

于 2013-04-22T06:23:42.313 に答える