usar son otra metodos metodo los llamar importar herencia ejemplos cuales como clases clase python python-3.x python-3.4 instance-method single-dispatch

son - importar clases en python



¿Cómo puedo usar functools.singledispatch con métodos de instancia? (2)

Python 3.4 added la capacidad de definir la sobrecarga de funciones con métodos estáticos. Este es esencialmente el ejemplo de la documentación:

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])

En su forma más pura, la implementación de singledispatch basa en el primer argumento para identificar el tipo, por lo que es complicado extender esta funcionalidad a los métodos de instancia.

¿Alguien tiene algún consejo sobre cómo usar (o jerry-rig) esta funcionalidad para que funcione con los métodos de instancia?


Al singledispatch la source de singledispatch , podemos ver que el decorador devuelve un wrapper() función wrapper() , que selecciona una función para llamar de las registradas según el tipo de args[0] ...

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

... lo cual está bien para una función normal, pero no es muy útil para un método de instancia, cuyo primer argumento siempre será self .

Sin embargo, podemos escribir un nuevo methdispatch decorador, que se basa en un singledispatch para hacer el trabajo pesado, pero en su lugar devuelve una función de envoltura que selecciona qué función registrada llamar según el tipo de 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

Aquí hay un ejemplo simple del decorador en uso:

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]

Observe que tanto el método get() decorado como el método registrado en la list tienen un autoargumento inicial como siempre.

Probando la clase Patchwork :

>>> pw = Patchwork(a=1, b=2, c=3) >>> pw.get("b") 2 >>> pw.get(["a", "c"]) [1, 3]


Un decorador es esencialmente un envoltorio que toma la función envuelta como argumento y devuelve otra función.

Como se indica en la respuesta aceptada, singledispatch devuelve un wrapper que toma el primer argumento como tipo registrado - self en métodos de instancia.

Como se muestra en esa respuesta, en casos como este puede escribir otro envoltorio para el parche de mono del decorador. Pero este tipo de soluciones hacky no siempre son la mejor opción.

Al igual que con cualquier otra función, puede llamar al contenedor y pasarle los argumentos explícitamente, lo que parece más simple, más plano y más legible para mí si este tipo de sobrecarga de métodos rara vez se realiza en un paquete.

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])

Hay otro módulo, singledispatch multipledispatch (no estándar, pero incluido en Anaconda y sin dependencias no estándar) que, como el nombre ya indica y a diferencia de singledispatch , permite multimétodos.

Además de los objetos Dispatcher , con la singledispatch compatible con singledispatch , proporciona un decorador de dispatch que oculta la creación y manipulación de estos objetos por parte del usuario.

El decorador de envío usa el nombre de la función para seleccionar el objeto Dispatcher apropiado al que agrega la nueva firma / función. Cuando encuentra un nuevo nombre de función, crea un nuevo objeto Dispatcher y almacena el par Nombre / Distribuidor en un espacio de nombres para referencia futura.

Por ejemplo:

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)