【问题标题】:Efficient keys for dictionaries字典的有效键
【发布时间】:2019-01-17 08:34:37
【问题描述】:

我是新来这里发帖的,所以我希望我能提供足够的细节。我试图找出关键选择是否会影响 Python 中字典的效率。我正在考虑的一些比较是:

  • 数字与字符串(例如 my_dict[20] 会比 my_dict['twenty'] 快)
  • len 的字符串(例如,my_dict['a'] 会比 my_dict['abcdefg'] 快)
  • 在字典中混合键类型,例如使用数字、字符串和/或元组(例如,my_dict = {0: 'zero', 2: 'two'} 的执行速度会比 {0: 'zero', 'two': 2} 快)

我无法从谷歌搜索中找到这个主题,所以我想这里的人可能知道。

【问题讨论】:

  • 对于查找使用整数哈希值。这使得该类型对性能的影响非常小。
  • 哈希对于几乎所有类型都非常快。差异将非常小。我不会担心的。
  • 看到这个非常相似的SO question(虽然不确定你的完全一样)。

标签: python dictionary key processing-efficiency memory-efficient


【解决方案1】:

我也不知道答案,但很容易测试。

from timeit import default_timer as timer
import string

#Create a few dictionaries, all the values are None
nums = range(1,27)
dict_numbers = dict.fromkeys(nums)

letters = string.ascii_lowercase
dict_singleLetter = dict.fromkeys(letters)

long_names = []
for letter in letters:
    long_names.append(''.join([letter, letters]))
dict_longNames = dict.fromkeys(long_names)

#Function to time thousands of runs to average out discrepancies
def testSpeed(dictionary, keys):
    x = None
    start = timer()
    for _ in range(1,100000):
        for i in keys:
            x = dictionary[i]
    end = timer()

    return str(end - start)

#Time the different dictionaries
print("Number took " + testSpeed(dict_numbers, nums) + " seconds")
print("Single letters took " + testSpeed(dict_singleLetter, letters) + " seconds")
print("Long keys took " + testSpeed(dict_longNames, long_names) + " seconds")

所有这些字典的长度相同,并且每个键包含相同的值。当我运行这个时,带有长键的字典实际上总是最快的,尽管可能只有 5%。这可能是由我不知道的其他细微差异造成的。数字和单个字母的速度非常接近,但数字通常比单个字母快不了多少。希望这能回答你的问题,并且这段代码应该很容易扩展以测试混合案例,但我现在没时间了。

【讨论】:

    【解决方案2】:

    首先,我建议您了解How are Python's Built In Dictionaries Implemented

    现在,让我们做一个小小的随机实验来证明这个理论(至少部分证明):

    import timeit
    import string
    import random
    import time
    
    
    def random_str(N):
        return ''.join(
            random.choice(string.ascii_uppercase + string.digits) for _ in range(N)
        )
    
    
    def experiment1(dct, keys):
        s = time.time()
        {dct[k] for k in keys}
        return time.time() - s
    
    
    if __name__ == "__main__":
        N = 10000
        K = 200
        S = 50000
        dct1 = {random_str(K): None for k in range(N)}
        dct2 = {i: None for i in range(N)}
        keys1 = list(dct1.keys())
        keys2 = list(dct2.keys())
    
        samples1 = []
        samples2 = []
        for i in range(S):
            samples1.append(experiment1(dct1, keys1))
            samples2.append(experiment1(dct2, keys2))
    
        print(sum(samples1), sum(samples2))
    
        # print(
        #     timeit.timeit('{dct1[k] for k in keys1}', setup='from __main__ import dct1, keys1')
        # )
    
        # print(
        #     timeit.timeit('{dct2[k] for k in keys2}', setup='from __main__ import dct2, keys2')
        # )
    

    我在盒子上使用不同样本大小得到的结果是:

    • N=10000, K=200, S=100 => 0.08300423622131348 0.07200479507446289
    • N=10000, K=200, S=1000 => 0.885051965713501 0.7120392322540283
    • N=10000, K=200, S=10000 => 8.88549256324768 7.005417346954346
    • N=10000, K=200, S=50000 => 43.57453536987305 34.82594871520996

    如您所见,无论您使用大随机字符串查找字典还是整数,性能都将保持几乎相同。您要考虑的唯一“真正”差异是两个词典的内存消耗。这在向/从磁盘转储/加载大量字典时可能是相关的,在这种情况下,拥有紧凑形式的数据结构可能是值得的,这样您就可以在缓存/读取它们时节省几秒钟的时间。

    NS:如果有人能够解释为什么我在使用 timeit(注释部分)时得到了非常大的时间,请让我......用很少的实验常数我会得到非常高的值......这就是我离开它的原因未注释。如果您知道原因,请添加评论;D

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-06
      • 2014-05-29
      • 2013-11-21
      • 2019-02-20
      • 2015-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多