Ответ 1
Используйте collections.defaultdict. Он разработан именно для этой цели.
Пусть есть метод, который будет кэшировать полученные результаты.
"Если" подход:
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 = {}
"За исключением" подхода:
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
"получить/имеет" подход:
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
Есть ли другой (более быстрый) способ? Какой из них наиболее pythonic? Какой из них вы бы использовали?
Примечание: разница в скорости:
calculate = calculateX # depening on test run
for i in xrange(10000):
calculate(datetime.utcnow())
Результаты time python test.py
:
calculate1: 0m9.579s
calculate2: 0m0.130s
calculate3: 0m0.095s
Используйте collections.defaultdict. Он разработан именно для этой цели.
Конечно; это Python: просто используйте defaultdict.
Хорошо, если вы пытаетесь что-то мемуаровать, лучше всего использовать класс Memoize и декораторы.
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]
Теперь определите некоторую функцию, которая будет сохранена в памяти, скажем, функция усиления ключа, которая скажет 100 000 md5sums хэшей строки:
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
Декоратор функции @Memoize эквивалентен определению функции, а затем определяет repeat_md5 = Memoize (repeat_md5). В первый раз, когда вы вызываете его для определенного набора аргументов, функция занимает около секунды для вычисления; и в следующий раз, когда вы вызываете его почти мгновенно, когда он читает его кеш.
Что касается метода memoization; если вы не делаете что-то глупое (например, первый метод, когда вы делаете "если ключ в some_dict.keys()", а не "if key in some_dict" ), не должно быть существенной разницы. (Первый метод плохой, поскольку вы сначала генерируете массив из словаря, а затем проверяете, есть ли в нем ключ, а не просто проверяете, находится ли ключ в dict (см. Кодирование как pythonista)). Кроме того, catching exceptions будет медленнее, чем если бы инструкции были выполнены (вам нужно создать исключение, обработчик которого должен обработать его, а затем вы его поймаете).