简体   繁体   English

Python 缓存:TypeError:不可散列的类型:'dict'

[英]Python Caching: TypeError: unhashable type: 'dict'

I am trying to implement a caching function in Python.我正在尝试在 Python 中实现缓存功能。 The code looks like this:代码如下所示:

def memoize(func):
    """Store the results of the decorated function for fast lookup
    """

    # Store results in a dict that maps arguments to results
    cache = {}

    def wrapper(*args, **kwargs):
        # If these arguments haven't been seen before, call func() and store the result.
        if (args, kwargs) not in cache:        
            cache[(args, kwargs)] = func(*args, **kwargs)          
        return cache[(args, kwargs)]

    return wrapper

@memoize
def add(a, b):
    print('Sleeping...')
    return a + b

add(1, 2)

When I run the code, I get TypeError: unhashable type: 'dict' .当我运行代码时,我得到TypeError: unhashable type: 'dict'

What's wrong?怎么了?

The key of a dict must be hashable. dict的键必须是可散列的。 You provide an unhashable key (args,kwargs) since kwargs is a dict which is unhashable.您提供了一个不可散列的键(args,kwargs)因为kwargs是一个不可散列的dict

To solve this problem, you should generate a hashable key from the combination of args and kwargs .为了解决这个问题,你应该从argskwargs的组合中生成一个可散列的键。 For example, you can use (assuming all values of args and kwargs are hashable)例如,您可以使用(假设argskwargs所有值都是可散列的)

key = ( args , tuple((kwargs.items())))

def memoize(func):
    """Store the results of the decorated function for fast lookup
    """

    # Store results in a dict that maps arguments to results
    cache = {}

    def wrapper(*args, **kwargs):
        # If these arguments haven't been seen before, call func() and store the result.
        key = ( args , tuple((kwargs.items())))
        if key not in cache:        
            cache[key] = cc = func(*args, **kwargs)          
            return cc
        return cache[key]

    return wrapper

@memoize
def add(a, b):
    print('Sleeping...')
    return a + b

print(add(1, 2))

This happens, because you are trying to put a dictionary as a key, which is a problem.发生这种情况是因为您试图将字典作为键,这是一个问题。 You can use frozenset() to froze the dictionary, so that it would您可以使用frozenset()来冻结字典,以便它

In this line:在这一行:

cache[(args, kwargs)] = func(*args, **kwargs)

you use kwargs which is dict as part of key.你使用kwargs ,它是dict作为密钥的一部分。 dict s are mutable and keys of dict s have to be immutable. dict是可变的,并且dict的键必须是不可变的。

Dict is not hashable, which means that you cannot use it in operations that require hash of the objects. Dict 不可散列,这意味着您不能在需要对对象进行散列的操作中使用它。 Using the object as a key in a dict is one of those things.使用对象作为字典中的键是其中之一。

In your case in specific, the awards are a dictionary and you are trying to use it as a part of a key for another dictionary.在您的具体情况下,奖项是一本字典,您正试图将其用作另一本字典的键的一部分。

To make it work, you should create a function that receives the awards and turn it into a number or a string that acts has a fingerprint for any dictionary with the same content.为了让它工作,你应该创建一个函数来接收奖励并将其转换为一个数字或一个字符串,该字符串对任何具有相同内容的字典都有指纹。

As a side note, this can also happen if you pass any dict as an argument, nameless, or keyword.作为旁注,如果您将任何 dict 作为参数、无名或关键字传递,也会发生这种情况。 If you chose to go with this solution, my advice is to do it in both args and kwargs and recursively check if you have any dict within your args.如果您选择使用此解决方案,我的建议是在 args 和 kwargs 中执行此操作,并递归检查您的 args 中是否有任何 dict。

As a second side note, on functools module, you have lru_cache for local cache, and you can use cachetools for cache operations and aiocache for an async cache that support the most popular backends other than local cache.其次,在 functools 模块上,您有 lru_cache 用于本地缓存,您可以将 cachetools 用于缓存操作,将 aiocache 用于支持除本地缓存之外的最流行后端的异步缓存。

All answers regarding this question still in many stackoverflow questions haven't accounted for the memoize feature of the code.在许多 stackoverflow 问题中,关于这个问题的所有答案都没有考虑到代码的记忆功能。 The line cache = {} cannot be placed inside the memoize decorator function. line cache = {}不能放在 memoize 装饰器函数中。 Otherwise, everytime you call the decorated function, cache will always initialize itself back to empty dict.否则,每次调用装饰函数时,缓存都会将自身初始化回空字典。

The code that utilize the memoize feature should look like this (I've tested it)使用 memoize 功能的代码应该是这样的(我已经测试过了)

cache = {}

def memoize(func):
    """Store the results of the decorated function for fast lookup
    """

    # Store results in a dict that maps arguments to results
    

    def wrapper(*args, **kwargs):
        # If these arguments haven't been seen before, call func() and store the result.
        global cache
        key = (args , tuple((kwargs.items())))
        if key not in cache:        
            cache[key] = func(*args, **kwargs)          
        return cache[key]
    return wrapper

@memoize
def add(a, b):
    print('Sleeping...')
    return a + b

add(1, 2)

The result for the first run is第一次运行的结果是

>>add(1, 2)
Sleeping...
3

If you run twice it will be如果您运行两次,它将是

>>add(1, 2)
3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM