web-dev-qa-db-ja.com

クラスメソッドのfunctools.partial

次のように別のより一般的なクラスメソッドを使用して、いくつかのクラスメソッドを定義しようとしています。

_class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = functools.partial(_color, type='_red')
    blue = functools.partial(_color, type='_blue')
    green = functools.partial(_color, type='_green')
_

しかし、これらのメソッドのいずれかを呼び出そうとすると、次のようになります。

_rgb = RGB(100, 192, 240)
print rgb.red()
TypeError: _color() takes exactly 2 arguments (1 given)
_

rgb.red(rgb)が機能するため、__color_にselfが渡されないと思います。

40
Arjor

メソッドではなく、関数でパーシャルを作成しています。 functools.partial()オブジェクトは記述子ではなく、それら自体はself引数を追加せず、メソッド自体として機能できません。バインドされたメソッドまたは関数をのみラップできますが、バインドされていないメソッドではまったく機能しません。これは 文書化

partialオブジェクトは、呼び出し可能で、弱参照可能で、属性を持つことができるという点でfunctionオブジェクトに似ています。いくつかの重要な違いがあります。たとえば、___name___および___doc___属性は自動的に作成されません。また、クラスで定義されたpartialオブジェクトは静的メソッドのように動作し、インスタンス属性のルックアップ中にバインドされたメソッドに変換されません。

代わりにpropertysを使用してください。これら記述子です:

_class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    @property
    def red(self): return self._color('_red')
    @property
    def blue(self): return self._color('_blue')
    @property
    def green(self): return self._color('_green')
_

Python 3.4以降、ここで新しい functools.partialmethod()オブジェクト を使用できます。インスタンスにバインドすると、次のようになります。

_class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = functools.partialmethod(_color, type='_red')
    blue = functools.partialmethod(_color, type='_blue')
    green = functools.partialmethod(_color, type='_green')
_

しかし、これらを呼び出す必要がありますが、propertyオブジェクトは単純な属性として使用できます。

47
Martijn Pieters

partialmethodの問題は、_inspect.signature_、_functools.wraps_、...と互換性がないことです。

奇妙なことに、 部分的なドキュメントの実装例 を使用して_functools.partial_を自分で再実装すると、機能します。

_# Implementation from:
# https://docs.python.org/3/library/functools.html#functools.partial
def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc
_
_class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = partial(_color, type='_red')
    blue = partial(_color, type='_blue')
    green = partial(_color, type='_green')

rgb = RGB(100, 192, 240)
print(rgb.red())  # Print red
_

その理由は、newfuncが_newfunc.__get___で 記述子プロトコル を実装する真の関数であるためです。一方、type(functools.partial)は、___call___が上書きされたカスタムクラスです。クラスはselfパラメータを自動的に追加しません。

0
Conchylicultor