Как я могу использовать functools.singledispatch с методами экземпляра?

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 основывается на первом аргументе для идентификации типа, поэтому затрудняет распространение этой функции на методы экземпляра.

Кто-нибудь имеет какие-либо советы относительно того, как использовать (или jerry-rig) эту функцию, чтобы заставить ее работать с методами экземпляра?

2 Solutions collect form web for “Как я могу использовать functools.singledispatch с методами экземпляра?”

Посмотрев на источник для singledispatch , мы видим, что декоратор возвращает функцию 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] 

Декоратор по существу является оберткой, которая принимает завернутую функцию в качестве аргумента и возвращает другую функцию.

Как указано в принятом ответе, singledispatch возвращает wrapper которая принимает первый аргумент как зарегистрированный тип – self в методах экземпляра.

Как показано в этом ответе, в подобных случаях вы можете написать еще одну обертку, чтобы обезглавить патч-декоратор. Но такие хакерские исправления не всегда являются лучшим вариантом.

Как и любая другая функция, вы можете вызвать оболочку и передать аргументы в нее явно, что кажется более простым, более плоским и более читаемым для меня, если такая перегрузка метода редко выполняется в пакете.

 from functools import singledispatch class TestClass(object): def __init__(self): self.test_method = singledispatch(self.test_method) self.test_method.register(int, self._test_method_int) self.test_method.register(list, self._test_method_list) def test_method(self, arg, verbose=False): if verbose: print("Let me just say,", end=" ") print(arg) def _test_method_int(self, arg): print("Strength in numbers, eh?", end=" ") print(arg) def _test_method_list(self, arg): print("Enumerate this:") for i, elem in enumerate(arg): print(i, elem) if __name__ == '__main__': test = TestClass() test.test_method(55555) test.test_method([33, 22, 11]) 

Есть еще один модуль, multipledispatch (не стандартный, но включенный в Anaconda и без каких-либо нестандартных зависимостей), который, поскольку имя уже указывает и в отличие от singledispatch , допускает singledispatch .

Помимо объектов Dispatcher , с синтаксисом singledispatch , он предоставляет singledispatch dispatch который скрывает создание и манипулирование этими объектами от пользователя.

Декоратор отправки использует имя функции для выбора соответствующего объекта Dispatcher, к которому он добавляет новую подпись / функцию. Когда он встречает новое имя функции, он создает новый объект Dispatcher и сохраняет имя / папку Dispatcher в пространстве имен для дальнейшего использования.

Например:

 from types import LambdaType from multipledispatch import dispatch class TestClass(object): @dispatch(object) def test_method(self, arg, verbose=False): if verbose: print("Let me just say,", end=" ") print(arg) @dispatch(int, float) def test_method(self, arg, arg2): print("Strength in numbers, eh?", end=" ") print(arg + arg2) @dispatch((list, tuple), LambdaType, type) def test_method(self, arg, arg2, arg3): print("Enumerate this:") for i, elem in enumerate(arg): print(i, arg3(arg2(elem))) if __name__ == '__main__': test = TestClass() test.test_method(55555, 9.5) test.test_method([33, 22, 11], lambda x: x*2, float) 
Interesting Posts

Запись CSV-файла с помощью умляутов, вызывающих «кодек UnicodeEncodeError: 'ascii', не может кодировать символ"

В чем разница между конструктором и инициализатором в python?

Проверьте, существует ли ключ и повторите попытку массива JSON с помощью Python

Запустить скрипт Python в выбранном файле

Python эквивалент вывода файла трубопровода на gzip в Perl, используя канал

В python, как я беру наивысшее появление чего-то в списке и сортирую его таким образом?

Как щелкнуть ссылку, которая имеет javascript: __ doPostBack in href?

Sqlite3, OperationalError: невозможно открыть файл базы данных

Передача переменной JavaScript в Python

Python & Scrapy: проблема с версией Scrapy

MongoDB aggregate / group / sum запрос переведен на pymongo запрос

Хороший язык для разработки игрового сервера?

Как использовать virtualenv с SDK Google App Engine в Mac OS X 10.6

Загрузка текстового файла, содержащего как float, так и строку, используя numpy.loadtxt

py2exe – генерировать один исполняемый файл

Python - лучший язык программирования в мире.