Подтвердить что ты не робот

Создание значений по умолчанию для словаря в python

Пусть есть метод, который будет кэшировать полученные результаты.

"Если" подход:

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
4b9b3361

Ответ 1

Используйте collections.defaultdict. Он разработан именно для этой цели.

Ответ 2

Конечно; это Python: просто используйте defaultdict.

Ответ 3

Хорошо, если вы пытаетесь что-то мемуаровать, лучше всего использовать класс 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 будет медленнее, чем если бы инструкции были выполнены (вам нужно создать исключение, обработчик которого должен обработать его, а затем вы его поймаете).