2010-07-31 18 views
10

¿Es una "buena práctica" crear una clase como la siguiente que pueda manejar el proceso de memorización para usted? Los beneficios de la memorización son tan grandes (en algunos casos, como este, donde pasa de llamadas de función 501003 a 1507 y de 1.409 a 0.006 segundos de tiempo de CPU en mi computadora) que parece que una clase como esta sería útil.Memoization Handler

Sin embargo, he leído solo comentarios negativos sobre el uso de eval(). ¿Es excusable este uso, dada la flexibilidad que ofrece este enfoque?

Esto puede guardar cualquier valor devuelto automáticamente a costa de perder efectos secundarios. Gracias.

import cProfile 

class Memoizer(object): 
    """A handler for saving function results.""" 
    def __init__(self): 
     self.memos = dict() 
    def memo(self, string): 
     if string in self.memos: 
      return self.memos[string] 
     else: 
      self.memos[string] = eval(string) 
      self.memo(string) 

def factorial(n): 
    assert type(n) == int 
    if n == 1: 
     return 1 
    else: 
     return n * factorial(n-1) 

# find the factorial of num 
num = 500 
# this many times 
times = 1000 

def factorialTwice(): 
    factorial(num) 
    for x in xrange(0, times): 
     factorial(num) 
    return factorial(num) 

def memoizedFactorial(): 
    handler = Memoizer() 
    for x in xrange(0, times): 
     handler.memo("factorial(%d)" % num) 
    return handler.memo("factorial(%d)" % num) 


cProfile.run('factorialTwice()') 

cProfile.run('memoizedFactorial()') 
+0

Estás hablando de "decoradores de Python" y la memorización es un uso fantástico para ellos. Y no requiere evaluaciones (que son parcialmente malas, has oído correctamente). – msw

Respuesta

13

Usted puede memoize sin tener que recurrir a eval.

A (muy básico) memoizer:

def memoized(f): 
    cache={} 
    def ret(*args): 
     if args in cache: 
      return cache[args] 
     else: 
      answer=f(*args) 
      cache[args]=answer 
      return answer 
    return ret 

@memoized 
def fibonacci(n): 
    if n==0 or n==1: 
     return 1 
    else: 
     return fibonacci(n-1)+fibonacci(n-2) 

print fibonacci(100) 
5

eval es a menudo mal escrito como evil principalmente porque la idea de ejecutar "cadenas" en tiempo de ejecución está cargada de consideraciones de seguridad. ¿Has escapado del código lo suficiente? ¿Comillas? Y una serie de otros dolores de cabeza molestos. Su manejador de memorando funciona, pero realmente no es la forma de Python de hacer las cosas. El enfoque de MAK es mucho más Pythonic. Probemos algunos experimentos.

Modifiqué ambas versiones y las hice funcionar una sola vez con 100 como entrada. También me mudé de la instanciación de Memoizer. Aquí están los resultados.

>>> timeit.timeit(memoizedFactorial,number=1000) 
0.08526921272277832h 
>>> timeit.timeit(foo0.mfactorial,number=1000) 
0.000804901123046875 

Además de esto, su versión requiere una envoltura alrededor de la la función que se memoised que debe ser escrito en una cadena. Eso es feo. La solución de MAK está limpia ya que el "proceso de memorización" está encapsulado en una función separada que se puede aplicar convenientemente a cualquier función cara de una manera discreta. Esto no es muy Ptónico. Tengo algunos detalles sobre cómo escribir decoradores en mi tutorial de Python al http://nibrahim.net.in/self-defence/ en caso de que esté interesado.