Ложноотрицательные результаты Python lru_cache

Я пытаюсь кэшировать функцию expand только по ее первому аргументу. Меня не волнуют значения других аргументов для кэширования.

Поскольку другие аргументы являются словарями, они не кэшируются, поэтому я определил класс, содержащий эти аргументы, чей хэш всегда возвращает 0, поэтому функция кэширования должна игнорировать его.

Я добавил код сокращения ниже. Я на Python версии 3.5.2.

class Node:
    def __init__(self, value):
        self.value = value

    def expand(self, a1, a2):
        return '{},{},{}'.format(self.value, a1, a2)


class ExpandArgs:
    def __init__(self, a1, a2):
        self.a1 = a1
        self.a2 = a2

    def __hash__(self):
        # We don't care about the hash, but it's required for caching
        return 0


@functools.lru_cache(maxsize=None)  # hash of args is always 0, so it should be ignored, and the hash of node should be used as the cache key
def expand(node, args):
    a1 = args.a1
    a2 = args.a2
    return node.expand(a1, a2)


e1 = ExpandArgs({}, {})
e2 = ExpandArgs({}, {})
print(hash(e1))  # 0
print(hash(e2))  # 0
node = Node(123)
print(expand.cache_info())  # CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)
expand(node, e1)
print(expand.cache_info())  # CacheInfo(hits=0, misses=1, maxsize=None, currsize=1)
expand(node, e2)
print(expand.cache_info())  # CacheInfo(hits=0, misses=2, maxsize=None, currsize=2)
expand(node, e1)
print(expand.cache_info())  # CacheInfo(hits=1, misses=2, maxsize=None, currsize=2)
expand(node, e2)
print(expand.cache_info())  # CacheInfo(hits=2, misses=2, maxsize=None, currsize=2)

Поскольку hash(e1) == hash(e2), я ожидаю, что второй вызов expand() столкнется с кэшированным значением для e1, но он промахивается.

Почему я не получаю 1 промах в кеше и 3 попадания в кеш для приведенного выше кода?


person philip238    schedule 12.05.2017    source источник
comment
Я полагаю, что мог бы сделать это вместо stackoverflow.com/a/32655449/3173255   -  person philip238    schedule 12.05.2017


Ответы (1)


Оказывается, eq используется вместо hash для проверки равенства аргументов функции в целях кэширования, поэтому, когда я меняю класс, он работает.

class ExpandArgs:
    def __init__(self, context, forecast_transaction_node_map, date_range_func):
        self.context = context
        self.forecast_transaction_node_map = forecast_transaction_node_map
        self.date_range_func = date_range_func

    def __hash__(self):
        # We don't care about the hash, but it's required for caching
        return 0

    def __eq__(self, other):
        return isinstance(other, self.__class__)
person philip238    schedule 12.05.2017