Pythonアクセサリー類の使用——bound/unbound method

3686 ワード

引用する
デコレーションクラスの方法を学習したところ、デコレーションクラスが__しかない場合call __メソッドは通常の関数(function)のみを装飾し,インスタンスメソッド(instance method)を装飾することはできず,pythonの関数実装と関係があることが分かった.本論文では,これについて議論を展開し,完全な装飾器クラス実装方法を提示する.
問題の発見
クラスのインスタンスメソッドを呼び出すと、インスタンスはメソッドの最初のパラメータselfとして使用されることを知っています.なお、以下の例では、実例の方法fooがデコレーション類Decoratorによりデコレーション後、Decorated()とする.foo('hello,world')の最初のselfはDecoratedインスタンスではなく、私たちが伝えた「hello,world」であり、明らかにこれは私たちの予想に合わない.実際にはpythonの関数呼び出しに関係し、bound method(インスタンス呼び出しによるメソッド)はインスタンス自体をメソッドの最初のパラメータとして自動的に入力しますが、functionおよびunbound method(クラス直接呼び出しのメソッド)はインスタンスを自動的に注入しません.デコレーションで装飾すると、Decoratorに入力されたfuncはboundプロパティを失い、自動的にインスタンスに注入されません.
class Decorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Decorator: self: {}, args: {}, kwargs: {}".format(self, args, kwargs))
        return self.func(*args, **kwargs)

class Decorated(object):
    @Decorator
    def foo(self, *args, **kwargs):
        print("Method Decorated: self: {}, args: {}, kwargs: {}".format(self, args, kwargs))

Decorated().foo('hello, world')
# Decorator: self: <__main__.decorator object="" at="">, args: ('hello, world',), kwargs: {}
# Method Decorated: self: hello, world, args: (), kwargs: {}

function、bound methodとunbound methodの違い
前節ではbound methodとunboud methodについて述べたが,この節ではこれらの概念を具体的に説明する.
 class Cls(object):

     def foo(self):
        pass
 instance = Cls()

 print 'foo' in Cls.__dict__                # True
 print 'foo' in instance.__dict__           # False

 print Cls.foo              # 
 print instance.foo         # >
 print Cls.__dict__['foo']  # 

fooはfunctionタイプでクラスの__dict__に存在するが,インスタンスアクセスによりbound methodが得られ,クラスアクセスによりunbound methodが得られることが分かった.これはpythonでは、すべての関数が非資料記述器(no data descriptor)であるためである.クラスまたはインスタンスを介してアクセスすると、__get__メソッドによってパッケージされ、unbound methodまたはbound methodに戻ります.unboudとboundの違いはmethodのim_です.selfが空かどうか.bound methodはinstanceを自動的に最初のパラメータとして使用しますが、unbound methodはパラメータに対して何も処理しません.前のセクションの例では、fooはDecoratorのメンバー変数として、typeはfunctionであるため、インスタンスは自動的に注入されません.
Cls.fooはCls.__dict __[‘foo’].__get__(None, Cls)に翻訳され、unbound method(im_self is None)instanceを返します.fooはtype(instance).__dic __[‘foo’].__get__(instance, type(instance))に翻訳され、bound method(im_self==instance)instance.foo(args, kwargs)Cls.foo(instance, args, kwargs)に等しいことを返します.
メソッドの変更
解析により,基本的な原因は,装飾されたfooがDecoratorインスタンスのメンバー変数として機能し,そのタイプはfunctionでありbound属性を失い,instanceインスタンスを関数の最初のパラメータに自動的に注入できないことにあることが分かった.これを解決するために、クラスまたはインスタンスによってアクセスするときにtypesを通過する記述器としてDecoratorを実装することができる.MethodTypeはDecoratorインスタンスをmethodにパッケージします.
class Decorator(object):

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        return types.MethodType(self, instance, owner)

    def __call__(self, *args, **kwargs):
        print("Decorator: self: {}, args: {}, kwargs: {}".format(self, args, kwargs))
        return self.func(*args, **kwargs)

参考文献
The Inside Story on New-Style Classes python - Instancemethod or function? - Stack Overflow Chris’s Wiki::blog/python/HowFunctionsToMethods Descriptor HowTo Guide — Python 2.7.15 documentation python - How can I decorate an instance method with a decorator class? - Stack Overflow Glossary — Python 2.7.15 documentation