2010-12-01 10 views
12

Tengamos un método que pueda almacenar en caché los resultados que calcula.Crear valores predeterminados para el diccionario en python

"Si" enfoque:

def calculate1(input_values): 
    if input_values not in calculate1.cache.keys(): 
     # do some calculation 
     result = input_values 
     calculate1.cache[input_values] = result 
    return calculate1.cache[input_values] 
calculate1.cache = {} 

"Excepto" enfoque:

def calculate2(input_values): 
    try: 
     return calculate2.cache[input_values] 
    except AttributeError: 
     calculate2.cache = {} 
    except KeyError: 
     pass 
    # do some calculation 
    result = input_values 
    calculate2.cache[input_values] = result 
    return result 

"GET/ha" enfoque:

def calculate3(input_values): 

    if not hasattr(calculate3, cache): 
     calculate3.cache = {} 

    result = calculate3.cache.get(input_values) 
    if not result: 
     # do some calculation 
     result = input_values 
     calculate3.cache[input_values] = result 
    return result 

¿Hay alguna otra forma (más rápida)? ¿Cuál es el más pythonic? ¿Cuál usarías?

Nota: Hay una diferencia de velocidad:

calculate = calculateX # depening on test run 
for i in xrange(10000): 
    calculate(datetime.utcnow()) 

Resultados: time python test.py

calculate1: 0m9.579s 
calculate2: 0m0.130s 
calculate3: 0m0.095s 
+0

su evaluación comparativa me parece sospechosa - No creo que el tercer método sea 100 veces más rápido. ¿Por casualidad reutilizaste el caché desde la primera vez? –

+0

No estoy seguro, pero la enorme disparidad entre la primera ejecución y las ejecuciones posteriores puede deberse a que el intérprete compila la secuencia de comandos (probablemente sea mejor hacerlo en Python y luego en su OS cargar, iniciar, etc.). –

+0

No. Eche un vistazo a http://files.myopera.com/ezimir/files/test.py Obteniendo '.keys()' es así de lento ... –

Respuesta

23

utilizar un collections.defaultdict. Está diseñado precisamente para este propósito.

+0

defaultdict parece lógico, pero me pregunto si es * más rápido * que otros métodos. A veces tuve malas sorpresas con tales extensiones de pitón – kriss

+6

A quién le importa si es más lento (incluso si lo es)? Es la solución correcta. Si se convierte en una botella, reemplácelo con una implementación ajustada a mano. Y si lo hizo en el stdlib, al menos su complejidad es aceptable. – delnan

+0

@kriss ' 0m0.101s' con el 'defaultdict' –

5

Por supuesto; esto es Python después de todo: solo usa un defaultdict.

2

Bueno, si intentas memorizar algo, es mejor usar una clase Memoize y decoradores.

class Memoize(object): 
    def __init__(self, func): 
     self.func = func 
     self.cache = {} 

    def __call__(self, *args): 
     if args not in self.cache: 
      self.cache[args] = self.func(*args) 
     return self.cache[args] 

Ahora definir una función que se memoized, por ejemplo una función de fortalecimiento clave que no dicen 100.000 md5sums de un hash de cadena:

import md5 

def one_md5(init_str): 
    return md5.md5(init_str).hexdigest() 

@Memoize 
def repeat_md5(cur_str, num=1000000, salt='aeb4f89a2'): 
    for i in xrange(num): 
     cur_str = one_md5(cur_str+salt) 
    return cur_str 

La función decoradora @Memoize es equivalente a la definición de la función y luego definiendo repeat_md5 = Memoize (repeat_md5). La primera vez que lo llama para un conjunto particular de argumentos, la función tarda aproximadamente un segundo en computarse; y la próxima vez que lo llame es casi instantáneo mientras lee desde su caché.

En cuanto al método de memorización; siempre y cuando no estés haciendo algo tonto (como el primer método en el que lo haces "si tecleas some_dict.keys()" en lugar de "if key in some_dict") no debería haber mucha diferencia significativa. (El primer método es malo, ya que primero se genera una matriz del diccionario y luego se comprueba si la clave está en él, en lugar de simplemente verificar si la clave está en el diccionario (Ver Coding like a pythonista)). También las excepciones de captura serán más lentas que las declaraciones de naturaleza (debe crear una excepción, luego el manejador de excepciones debe manejarla y luego la captura).

+1

* Código Like a Pythonista * es un gran recurso, gracias. –

Cuestiones relacionadas