【问题标题】:setdefault vs defaultdict performancesetdefault 与 defaultdict 性能
【发布时间】:2016-12-02 05:03:29
【问题描述】:

我正在为性能很重要的应用程序编写代码。我想知道为什么defaultdict 似乎比setdefault 更快。

我希望能够使用setdefault,主要是因为我不喜欢嵌套defaultdict 的打印输出(参见下面的实现)。

在我的代码中,我需要测试element_id 是否已经是字典的键。

这是我正在测试的两个函数:

def defaultdictfunc(subcases,other_ids,element_ids):
    dict_name= defaultdict(lambda: defaultdict(lambda: defaultdict(dict)))
    for subcase in subcases:
        for other_id in other_ids:
            for element_id in element_ids: 
                if element_id in dict_name[subcase][other_id]:
                    # error duplicate element_id
                    pass
                else:
                    dict_name[subcase][other_id][element_id]=0
    return dict_name

def setdefaultfunc(subcases,other_ids,element_ids):
    dict_name={}
    for subcase in subcases:
        for other_id in other_ids:
            for element_id in element_ids: 
                if element_id in dict_name.setdefault(subcase,{}).setdefault(other_id,{}):
                    # error duplicate element_id
                    pass
                else:
                    dict_name[subcase][other_id][element_id]=0

    return dict_name

IPython 输入输出:

In [1]: from numpy.random import randint

In [2]: subcases,other_ids,element_ids=(randint(0,100,100),randint(0,100,100),randint(0,100,100))

In [5]: from collections import defaultdict

In [6]: defaultdictfunc(subcases,other_ids,element_ids)==setdefaultfunc(subcases,other_ids,element_ids)
Out[6]: True

In [7]: %timeit defaultdictfunc(subcases,other_ids,element_ids)
10 loops, best of 3: 177 ms per loop

In [8]: % timeit setdefaultfunc(subcases,other_ids,element_ids)
1 loops, best of 3: 351 ms per loop

为什么setdefaultfunc 更慢。我认为底层代码是一样的。有没有办法提高它的速度?

谢谢

【问题讨论】:

  • The docs,一定要说,“[使用defaultdict] 比使用dict.setdefault() 的等效技术更简单、更快。”要解决打印问题,使用dict(dict_name)defaultdict 转换回常规dict 很容易(且快速)。
  • defaultdictdict.setdefault() 更快是有道理的,因为前者在创建时为整个 dict 设置默认值,而 setdefault() 在读取时为每个元素执行它。使用setdefault 的一个原因是当您分配的默认值是基于键(或其他东西)而不是整个字典的通用默认值时。
  • 谢谢大家,现在很有意义。

标签: python dictionary defaultdict setdefault


【解决方案1】:

根据用户aneroid

defaultdictdict.setdefault() 更快是有道理的,因为前者在创建时为整个 dict 设置默认值,而 setdefault() 在读取时为每个元素执行它。使用 setdefault 的一个原因是当您分配的默认值是基于键(或其他东西)而不是整个 dict 的通用默认值时。

【讨论】:

    【解决方案2】:

    setdefaultfunc 最糟糕,因为您在循环中多次调用 dict 构造函数(因为{} 等效于dict()),而defaultdict 通过自己的设计避免了这种情况。

    稍加改动,就可以轻松改进setdefaultfunc

    def setdefaultfunc2(subcases,other_ids,element_ids):
        dict_name={}
        for subcase in subcases:
            subcase_dict = dict_name.setdefault(subcase,{})
            for other_id in other_ids:
                other_id_dict = subcase_dict.setdefault(other_id,{})
                for element_id in element_ids: 
                    if element_id in other_id_dict:
                        # error duplicate element_id
                        pass
                    else:
                        other_id_dict[element_id]=0
        return dict_name
    

    通过此更改,我的机器中的结果是:

    In [37]: defaultdictfunc(subcases,other_ids,element_ids)==setdefaultfunc2(subcases,other_ids,element_ids)
    Out[37]: True
    
    In [38]: %timeit defaultdictfunc(subcases,other_ids,element_ids)
    286 ms ± 8.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [39]: %timeit setdefaultfunc(subcases,other_ids,element_ids)
    434 ms ± 1.78 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [40]: %timeit setdefaultfunc2(subcases,other_ids,element_ids)
    174 ms ± 348 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    IMO,defaultdict 没有提供足够的性能提升以使其值得使用。

    【讨论】:

    • 看起来不太合适。什么时候你会以同样的方式优化 defaultdict 解决方案?
    • @superbrain 你的请求对我来说没有意义。当您使用 defaultdict(而不是 setdefault)时,您无法控制 dict 何时实例化(您在构造函数中传递对类的引用,并在需要时处理其余部分)。因此,如果您创建了一个复杂的构造函数(如“defaultdictfunc”函数所做的那样),或者如果您在每次迭代中使用多个简单的构造函数(如“defaultdictfunc2”那样),结果将是相同的,甚至是最差的。我确实没有测试,但我觉得没有必要尝试,因为没有证据表明这会改善任何事情。
    • defaultdictfunc 中,您在最里面的循环中一遍又一遍地重新访问dict_name[subcase][other_id]。为了进行适当的比较,您应该将其存储在变量 other_id_dict 中,就像在 setdefaultfunc2 中所做的那样。 subcase_dict 也是如此,不过影响要小得多。
    • @superbrain Python dict的访问操作是恒定的,即O(1)。直接或通过辅助变量访问它对性能没有影响。检查这个:stackoverflow.com/questions/1963507/…
    • O(1) 和 O(0) 相同。例如,常数 1-函数在 O(1) 中,但 not 在 O(0) 中。但你甚至不需要明白这一点。再说一遍:做某事比什么都不做需要更多的时间。就这么简单。
    猜你喜欢
    • 1970-01-01
    • 2017-04-18
    • 2012-09-15
    • 2013-11-21
    • 1970-01-01
    • 2013-05-14
    • 1970-01-01
    • 2022-11-20
    • 2013-10-11
    相关资源
    最近更新 更多