5

次のコードで、クラス変数をメソッド ポインターとして使用すると、バインドされていないメソッド エラーが発生するのに、通常の変数を使用すると正常に動作するのはなぜですか。

class Cmd: 
    cmd = None

    @staticmethod   
    def cmdOne():
        print 'cmd one'

    @staticmethod   
    def cmdTwo():
        print 'cmd two'

def main():
    cmd = Cmd.cmdOne
    cmd() # works fine

    Cmd.cmd = Cmd.cmdOne        
    Cmd.cmd() # unbound error !!

if __name__=="__main__":
    main()

完全なエラー:

TypeError: unbound method cmdOne() must be called with Cmd instance as 
           first argument (got nothing instead)
4

3 に答える 3

2

この動作を「ボトムアップ」から見るのが好きです。

Python の関数は、「記述子オブジェクト」として機能します。このように、__get__()メソッドがあります。

このようなメソッドを持つクラス属性への読み取りアクセスは、__get__()このメソッドに「リダイレクト」されます。クラスへの属性アクセスは として実行されattribute.__get__(None, containing_class)、インスタンスへの属性アクセスは にマップされattribute.__get__(instance, containing_class)ます。

関数のメソッドのタスクは、インスタンスへの属性アクセスの場合__get__()、パラメーターをラップするメソッド オブジェクトで関数をラップすることです。selfこれをバインドメソッドと呼びます。

2.x のクラス属性アクセスでは、関数__get__()はバインドされていないメソッド ラッパーを返しますが、今日学んだように、3.x ではそれ自体を返します。(__get__()メカニズムは 3.x にも存在しますが、関数はそれ自体を返すだけであることに注意してください。) 呼び出し方を見るとほぼ同じですが、バインドされていないメソッド ラッパーがself引数の正しい型を追加でチェックします。

呼び出しは、最初に指定されたオブジェクトを返すように設計されstaticmethod()たオブジェクトを作成するだけで、記述された動作を取り消すことができます。__get__()これがHYRY のトリックのしくみです: 属性 acces がstaticmethod()ラッピングを元に戻し、呼び出しが再度行われるため、「新しい」属性は古い属性と同じステータスになりますが、この場合はstaticmethod()2 回適用されているように見えます (実際にはそうではありません) )。

(ところで:この奇妙なコンテキストでも機能します:

s = staticmethod(8)
t = s.__get__(None, 2) # gives 8

ただし8、関数で2もクラスでもありません。)

あなたの質問では、2 つの状況があります。

cmd = Cmd.cmdOne
cmd() # works fine

クラスにアクセスし、そのcmdOne属性であるstaticmethod()オブジェクトを要求します。これは its を介してクエリされ__get__()、元の関数が返され、それが呼び出されます。それがうまく機能する理由です。

Cmd.cmd = Cmd.cmdOne
Cmd.cmd() # unbound error

は同じことを行いますが、この機能を に割り当てますCmd.cmd。次の行は属性アクセスです。これもまた、__get__()関数自体を呼び出してバインドされていないメソッドを返します。このメソッドは、正しいselfオブジェクトを最初の引数として呼び出す必要があります。

于 2013-04-27T05:43:40.487 に答える