proyectos ejemplos python class methods callable

python - ejemplos - django



Añadiendo dinámicamente invocables a la clase como "método" de instancia (2)

El problema con el que se está metiendo es que su objeto no está vinculado como un método de la clase Baz en la que lo está insertando. Esto se debe a que no es un descriptor, ¡ qué funciones regulares son !

Puede solucionar esto agregando un método simple __get__ a su clase Foo que lo convierte en un método cuando se accede como un descriptor:

import types class Foo(object): # your other stuff here def __get__(self, obj, objtype=None): if obj is None: return self # unbound else: return types.MethodType(self, obj) # bound to obj

Implementé una metaclase que desglosa los atributos de clase para las clases creadas con él y construye métodos a partir de los datos de esos argumentos, luego adjunta esos métodos creados dinámicamente directamente al objeto de clase (la clase en cuestión permite una fácil definición de objetos de formularios web para uso en un marco de prueba web). Ha funcionado bien, pero ahora necesito agregar un tipo de método más complejo que, para mantener las cosas limpias, lo implementé como una clase invocable. Lamentablemente, cuando trato de llamar a la clase invocable en una instancia, se trata como un atributo de clase en lugar de un método de instancia, y cuando se llama, solo recibe su propio self . Puedo ver por qué sucede esto, pero esperaba que alguien pudiera tener una solución mejor que las que se me ocurrieron. Ilustración simplificada del problema:

class Foo(object): def __init__(self, name, val): self.name = name self.val = val self.__name__ = name + ''_foo'' self.name = name # This doesn''t work as I''d wish def __call__(self, instance): return self.name + str(self.val + instance.val) def get_methods(name, foo_val): foo = Foo(name, foo_val) def bar(self): return name + str(self.val + 2) bar.__name__ = name + ''_bar'' return foo, bar class Baz(object): def __init__(self, val): self.val = val for method in get_methods(''biff'', 1): setattr(Baz, method.__name__, method) baz = Baz(10) # baz.val == 10 # baz.biff_foo() == ''biff11'' # baz.biff_bar() == ''biff12''

He pensado en:

  1. Usando un descriptor, pero parece mucho más complejo de lo que es necesario aquí
  2. Usar un cierre dentro de una fábrica para foo , pero los cierres anidados son reemplazos feos y desordenados para los objetos la mayor parte del tiempo, imo
  3. Envolviendo la instancia de Foo en un método que pasa a la instancia de Foo como instance , básicamente decorador, eso es lo que realmente agrego a Baz , pero eso parece superfluo y básicamente una forma más complicada de hacer lo mismo que ( 2)

¿Hay alguna manera mejor que cualquiera de estos para tratar de lograr lo que quiero, o debería simplemente morder la bala y usar algún tipo de patrón de fábrica de cierre?


Una forma de hacerlo es adjuntar los objetos invocables a la clase como métodos independientes. El constructor del método trabajará con callables arbitrarios (es decir, instancias de clases con un __call__() ), no solo funciones.

from types import MethodType class Foo(object): def __init__(self, name, val): self.name = name self.val = val self.__name__ = name + ''_foo'' self.name = name def __call__(self, instance): return self.name + str(self.val + instance.val) class Baz(object): def __init__(self, val): self.val = val Baz.biff = MethodType(Foo("biff", 42), None, Baz) b = Baz(13) print b.biff() >>> biff55

En Python 3, no hay tal cosa como una instancia independiente (las clases solo tienen funciones regulares adjuntas) por lo que podría hacer que su clase Foo un descriptor que devuelva un método de instancia enlazado dándole un __get__() . (En realidad, ese enfoque también funcionará en Python 2.x, pero lo anterior funcionará un poco mejor).

from types import MethodType class Foo(object): def __init__(self, name, val): self.name = name self.val = val self.__name__ = name + ''_foo'' self.name = name def __call__(self, instance): return self.name + str(self.val + instance.val) def __get__(self, instance, owner): return MethodType(self, instance) if instance else self # Python 2: MethodType(self, instance, owner) class Baz(object): def __init__(self, val): self.val = val Baz.biff = Foo("biff", 42) b = Baz(13) print b.biff() >>> biff55