【问题标题】:Create default values for dictionary in python在python中为字典创建默认值
【发布时间】:2010-12-01 15:46:03
【问题描述】:

让我们有一个方法可以缓存它计算的结果。

“如果”方法

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

“get/has”方法

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

【问题讨论】:

  • 您的基准测试对我来说看起来很可疑 - 我不相信第三种方法快 100 倍。您是否有机会重用第一次运行时的缓存?
  • 使用键确实会变慢,至少在 python 2 中(它生成一个列表)。这也意味着线性搜索。为什么TF不只使用input_values not in calculate1.cache?这是一个简单的哈希查找,并且可能与其他哈希查找相近(例如,
  • 如果你想测量 Python 代码的执行时间,你可以使用timeit 模块,它可能会给你比time 更准确的答案。
  • 您的基准测试似乎不合适;至少在我的系统上没有使用缓存中的值,因为每个循环需要超过一微秒。添加 print len(calculate.cache) 并尝试偶尔检查缓存。也许 datetime.utcnow().microsecond % 500
  • 嗯;我只是不认为如果你只是想做的是记忆,那么 defaultdict 特别会有所帮助。并且 memoization 可能最好在 python 中使用装饰器类来完成。见下文。

标签: python coding-style performance


【解决方案1】:

使用collections.defaultdict。它正是为此目的而设计的。

【讨论】:

  • defaultdict 似乎合乎逻辑,但我想知道它是否比其他方法 faster ?有时我会对这样的 python 扩展感到意外。
  • 谁在乎它是否更慢(即使它是)?这是正确的解决方案。如果它成为瓶颈,则用手动调整的实现替换它。如果它进入标准库,至少它的复杂性是可以接受的。
  • @kriss 0m0.101sdefaultdict
  • @delnan:OP 要求性能,显然他在乎。除此之外,我不同意 pythonic 的心态there is (only) **One** right solution,这是主观的,我想知道我使用的是否简洁、简单、快速、同时使用多个……并做出明智的选择。但在这种情况下,defaultdict 显然具有简洁、高效和简单的特点。
  • 无论如何,这正是我正在寻找的(与 OP 的问题不同)问题的答案——我现在不关心性能,只关心可读性/可维护性。谢谢,乌努布! :)
【解决方案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)。第一次为一组特定的参数调用它时,该函数需要大约一秒钟的时间来计算;并且下次当它从缓存中读取时调用它几乎是瞬时的。

    关于记忆的方法;只要你不做傻事(比如你做if key in some_dict.keys()而不是if key in some_dict的第一种方法)就不应该有太大的区别。 (第一种方法不好,因为您首先从字典中生成一个数组,然后检查键是否在其中;而不仅仅是检查键是否在字典中(参见Coding like a pythonista))。捕获异常也会比 if 语句本质上要慢(您必须创建一个异常,然后异常处理程序必须处理它;然后您捕获它)。

    【讨论】:

      猜你喜欢
      • 2016-08-26
      • 2021-12-20
      • 2021-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      • 2011-06-29
      • 2016-06-25
      相关资源
      最近更新 更多