2010-03-23 9 views
6

tengo una función decorada (versión simplificada):añadir una función de decorar a una clase

class Memoize: 
    def __init__(self, function): 
     self.function = function 
     self.memoized = {} 
    def __call__(self, *args, **kwds): 
     hash = args 
     try: 
      return self.memoized[hash] 
     except KeyError: 
      self.memoized[hash] = self.function(*args) 
      return self.memoized[hash] 


@Memoize 
def _DrawPlot(self, options): 
    do something... 

ahora quiere añadir este método a una clase de pre-esisting.

ROOT.TChain.DrawPlot = _DrawPlot 

cuando llamo a este método:

chain = TChain() 
chain.DrawPlot(opts) 

que tengo:

self.memoized[hash] = self.function(*args) 
TypeError: _DrawPlot() takes exactly 2 arguments (1 given) 

¿por qué no se propagan auto?

+0

Siempre herede de 'object' en lugar de nada para que esté utilizando clases de estilo nuevo. –

+0

No nombre métodos con mayúsculas iniciales si tiene alguna opción en la materia. Use nombres como '_draw_plot' (recomendado por PEP 8) o' _drawPlot'. –

+0

Mi respuesta fue engañosa, por lo que obtuvo el corte. Gracias por los comentarios Mike! –

Respuesta

3

El problema es que ha definido su propia clase invocable y luego trató de usarla como método. Cuando utiliza una función como atributo, acceder a la función como un atributo lo llama su método __get__ para devolver algo que no sea la función en sí misma: el método vinculado. Cuando tiene su propia clase sin definir __get__, simplemente devuelve su instancia sin pasar implícitamente self.

Los descriptores se explican algunos en http://docs.python.org/reference/datamodel.html#descriptors si no está familiarizado con ellos. Los métodos __get__, __set__ y __delete__ cambian la forma de interactuar con su objeto como funciona un atributo.


Se podría aplicar memoize como una función y el uso de la incorporada en el __get__ magia que funciona ya tienen

import functools 

def memoize(f): 
    @functools.wraps(f) 
    def memoized(*args, _cache={}): 
     # This abuses the normally-unwanted behaviour of mutable default arguments. 
     if args not in _cache: 
      _cache[args] = f(*args) 
     return _cache[args] 
    return memoized 

o mediante la modificación de su clase en la línea de

import functools 

class Memoize(object): #inherit object 
    def __init__(self, function): 
     self.function = function 
     self.memoized = {} 
    def __call__(self, *args): #don't accept kwargs you don't want. 
     # I removed "hash = args" because it shadowed a builtin function and 
     # because it was untrue--it wasn't a hash, it was something you intended for 
     # Python to hash for you. 
     try: 
      return self.memoized[args] 
     except KeyError: 
      self.memoized[args] = self.function(*args) 
      return self.memoized[args] 
    def __get__(self, obj, type): 
     if obj is None: #We looked up on the class 
      return self 

     return functools.partial(self, obj) 

Nota que ambos se ahogan si cualquiera de los argumentos que pasas es mutable (bueno, técnicamente no se puede modificar). Esto podría ser adecuado para su caso, pero es posible que también desee tratar el caso en el que args es ininterrumpido.

+0

Prefiero la segunda versión, porque puedo controlar la memoria caché, puedo crear más de un objeto Memorize y así diversificar la memoria caché para diferentes funciones. Estoy usando algo más complicado que 'hash = args', porque necesito manejar objetos mutables. Como dijiste "hash" no es un nombre muy bueno. –

+0

@wiso, El primer ejemplo de código tendría una memoria caché diferente para cada función que haya memorizado. Siempre tenga cuidado al tratar de manejar tipos mutables en la memorización; no son lavables por una buena razón. Debes entender cómo funciona la función * being * memoized para saber si tiene errores o no. –

+0

lo siento, pero su solución no funcionan: @Memoize función DEF (self, x): de impresión auto return x * x my_class clase: pase my_class.do = función c = my_class() imprimir c.do (2) TypeError: function() toma exactamente 2 argumentos (1 dado) –

Cuestiones relacionadas