【问题标题】:Any changes in 'functools.lru_cache' from python 2 and python 3?python 2和python 3的'functools.lru_cache'有什么变化吗?
【发布时间】:2020-03-04 01:33:23
【问题描述】:

我的代码在python2.7 中运行良好,但在python 3 中失败 functools.lru_cache(maxsize=32) 从 python 2 到 python 3 的任何更改。

我得到的错误是我的configparser对象在缓存functools.lru_cache它说

TypeError: unhashable type: 'ConfigParser'

想了解 'functools.lru_cache' 从 python 2 和 python 3 的变化吗?

#CONFI FILE
[translate]
api_url = https://url
api_version = version_num
api_key = key_value


#code goes here
import functools
from configparser import ConfigParser as SafeConfigParser
config = SafeConfigParser()
path ="./conf/services.ini"
config.read(path)


@functools.lru_cache(maxsize=32)
def build_api_params_key(config):
    """Build the api url and return with key."""
    api_url = config.get('translate', 'api_url')
    api_version = config.get('translate', 'api_version')
    api_key = config.get('translate', 'api_key')
    full_api_url = api_url + api_version

    return api_key

【问题讨论】:

  • cache = {} cache_get = cache.get .... make_key = _make_key # 从函数中构建一个键 arguments key = make_key(args, kwds, typed) result = cache_get(key, sentinel) 错误是因为while缓存解析器对象被用作dict 键,并且由于该对象不可散列,因此会引发错误。如果我们有一个不可散列的对象作为参数,还有其他使用 functools.lru_cache 的方法吗?
  • 是的,Python 2.7 使用functools32.lru_cache 我尝试使用functools.lru_cache,因为functools32 在python 3 中被重命名为functools
  • functools32 未重命名为 functoolsfunctools32 是后来的functools 功能的backport

标签: python python-3.x python-2.7 functools


【解决方案1】:

这里的问题不是functools.lru_cache,实际上是ConfigParser。 ConfigParser 继承自 RawConfigParser,在 Python 3x 中,继承自 collections.abc.MutableMappingMutableMapping 抽象类不可散列,因为它是可变的并且不实现 __hash__ 魔术方法。

由于ConfigParser 实例不可散列,因此它不能用作functools.lru_cache 装饰器中缓存字典的键。

如需进一步参考,请参阅this section of the configparser docs

假设需要缓存配置文件的内容,另一种选择是读取文件的内容,然后将内容字符串传递给缓存的函数,就像这样

import functools
from configparser import ConfigParser as SafeConfigParser
path = "./conf/services.ini"
config_contents = open(path).read()

@functools.lru_cache(maxsize=32)
def build_api_params_key(config_contents: str):
    """Build the api url and return with key."""
    config = SafeConfigParser()
    config.read_string(config_contents)
    api_url = config.get('translate', 'api_url')
    api_version = config.get('translate', 'api_version')
    api_key = config.get('translate', 'api_key')
    full_api_url = api_url + api_version

    return api_key

在上述解决方案中,读取配置文件以获取包含其内容的字符串。由于字符串是可散列的,因此可以将其传递给缓存函数。如果您希望在函数中读取文件的内容,也可以对文件指针执行类似的操作。但是,这些解决方案与 Python 2.7 不兼容,因为未定义 read_string

【讨论】:

  • 这确实是一个很好的解决方法。但我在这里找到了一些东西,stackoverflow.com/questions/4669391/… 我喜欢这种方法,想和它一起分享。
  • @arunnaray 谢谢你分享这个。这是使用缓存装饰器缓存不可散列参数的一个很好的解决方案。由于您的问题涉及 Python 2 和 3 之间的区别,您会说这回答了您的问题吗?
  • @arunnaray 如果性能是一个问题,我还应该指出,就问题中的代码而言,使用 pickle 对参数进行散列会带来大量开销。事实上,它会导致足够的开销,以至于 MemoizeMutable 修饰函数比没有任何缓存的函数慢大约 3 倍。
  • configparser PyPI 包向后移植了许多 Python 3 更改,它也继承自 MutableMapping,但在 Python 2 中仍然可以散列。我怀疑这个故事稍微复杂一些——也许这个 ABC 才刚刚开始在 Python 3 中进行检查?由于从来没有明确的__hash__ 它可能会阻止默认的id 使用。
猜你喜欢
  • 2020-05-06
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多