52

Python 3.4では、静的メソッドで関数のオーバーロードを定義する機能が追加されました。これは基本的にドキュメントの例です:

from functools import singledispatch


class TestClass(object):
    @singledispatch
    def test_method(arg, verbose=False):
        if verbose:
            print("Let me just say,", end=" ")

        print(arg)

    @test_method.register(int)
    def _(arg):
        print("Strength in numbers, eh?", end=" ")
        print(arg)

    @test_method.register(list)
    def _(arg):
        print("Enumerate this:")

        for i, elem in enumerate(arg):
            print(i, elem)

if __name__ == '__main__':
    TestClass.test_method(55555)
    TestClass.test_method([33, 22, 11])

最も純粋な形式では、singledispatch実装は型を識別するために最初の引数に依存しているため、この機能をインスタンス メソッドに拡張するのは困難です。

この機能を使用 (またはジェリーリグ) してインスタンス メソッドで動作させる方法について誰かアドバイスはありますか?

4

2 に答える 2

78

更新: Python 3.8 以降functools.singledispatchmethod、メソッド、クラスメソッド、抽象メソッド、および静的メソッドで単一のディスパッチが許可されます。

古い Python バージョンについては、この回答の残りを参照してください。

ソースsingledispatch見ると、デコレータが function を返すことがわかりますwrapper()。これは、型に基づいて登録されたものから呼び出す関数を選択しますargs[0]...

    def wrapper(*args, **kw):
        return dispatch(args[0].__class__)(*args, **kw)

...これは通常の関数には問題ありませんが、最初の引数が常に になるインスタンスメソッドにはあまり使用されませんself

methdispatchただし、面倒な作業を行うために依存する新しい decorator を作成することはできsingledispatchますが、代わりに、の型に基づいて呼び出す登録済み関数を選択するラッパー関数を返しますargs[1]

from functools import singledispatch, update_wrapper

def methdispatch(func):
    dispatcher = singledispatch(func)
    def wrapper(*args, **kw):
        return dispatcher.dispatch(args[1].__class__)(*args, **kw)
    wrapper.register = dispatcher.register
    update_wrapper(wrapper, func)
    return wrapper

使用中のデコレータの簡単な例を次に示します。

class Patchwork(object):

    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    @methdispatch
    def get(self, arg):
        return getattr(self, arg, None)

    @get.register(list)
    def _(self, arg):
        return [self.get(x) for x in arg]

装飾されget()たメソッドと登録されたメソッドの両方が、通常どおりlist初期引数を持つことに注意してください。self

Patchworkクラスのテスト:

>>> pw = Patchwork(a=1, b=2, c=3)
>>> pw.get("b")
2
>>> pw.get(["a", "c"])
[1, 3]
于 2014-07-07T02:37:35.440 に答える