【问题标题】:Performance in Python 3 dictionary iteration: dict[key] vs. dict.items()Python 3 字典迭代中的性能:dict[key] 与 dict.items()
【发布时间】:2019-04-21 07:50:12
【问题描述】:

其中哪个更快,为什么?或者他们是一样的?答案是否因任何条件(字典大小、数据类型等)而异?

传统:

for key in dict:
    x = dict[key]
    x = key

潮人:

for key, value in dict.items():
    y = value
    y = key

我还没有看到完全相同的副本,但如果有的话,我很乐意指出它。

【问题讨论】:

  • “Hipster”版本更加 Pythonic,所以我猜想在 CPython 解释器中已经得到了更好的优化,但是请先了解一下。
  • @gilch 这也是我的猜测,但我不确定。如果我有样本数据,有任何计时经验,并且知道什么好的数据集可能看起来像分析边缘情况/约束,我会自己做测试。
  • @jpp 稍微修改了问题
  • IPython 有一个非常方便的%timeit 魔法来进行这种分析。你可以很容易地用dict(zip(range(foo), range(foo))) 之类的东西为一些大数字 foo 制作一个巨大的字典。
  • “传统”的问题在于它在每个循环中都进行了额外的字典查找。我认为这很快,但不如key, value 本地人那么快。尽管您可能必须在函数内部执行此操作才能看到很多加速,因为在模块级别,“本地”只是全局变量,需要 dict 查找。

标签: python performance dictionary iteration


【解决方案1】:

事实证明,实际上存在数量级的差异。

我对性能测试了解不多,但我尝试创建 3 个不同大小的 dict,每个较小的 dict 都是较大 dict 的子集。然后,我通过两个函数(传统与时髦)运行所有三个 dicts。然后我做了 100 次。

dict1、dict2 和 dict3 的字典大小(键值对数)分别为 1000、50000、500000。

似乎存在显着差异,d.items() 通常更快d.items() 在较小的字典上WAY更快。这符合预期(Python 通常奖励“pythonic”代码)。

结果:

--d[key]--
dict1 -- mean: 0.0001113555802294286, st. dev: 1.9951038526222054e-05
dict2 -- mean: 0.01669296698019025, st. dev: 0.019088713496142
dict3 -- mean: 0.2553815016898443, st. dev: 0.02778986771642094

--d.items()--
dict1 -- mean: 6.005059978633653e-05, st. dev: 1.1960199272812617e-05
dict2 -- mean: 0.00507106617995305, st. dev: 0.009871762371401046
dict3 -- mean: 0.07369932165958744, st. dev: 0.023440325168927384

提供结果的代码 (repl.it):

import timeit
import random
import statistics

def traditional(dicty):

  for key in dicty:
    x = dicty[key]
    x = key

def hipster(dicty):

  for key, value in dicty.items():
    y = value
    y = key

def generate_random_dicts():
  random_dict1, random_dict2, random_dict3 = {}, {}, {}

  for _ in range(1000):
    key = generate_random_str_one_to_ten_chars()
    val = generate_random_str_one_to_ten_chars()
    random_dict1[key] = val
    random_dict2[key] = val
    random_dict3[key] = val

  for _ in range(49000):
    key = generate_random_str_one_to_ten_chars()
    val = generate_random_str_one_to_ten_chars()
    random_dict2[key] = val
    random_dict3[key] = val

  for _ in range(450000):
    key = generate_random_str_one_to_ten_chars()
    val = generate_random_str_one_to_ten_chars()
    random_dict3[key] = val

  return [random_dict1, random_dict2, random_dict3]

def generate_random_str_one_to_ten_chars():
  ret_str = ""
  for x in range(random.randrange(1,10,1)):
    ret_str += chr(random.randrange(40,126,1))
  return ret_str

dict1, dict2, dict3 = generate_random_dicts()

test_dicts = [dict1, dict2, dict3]

times = {}
times['traditional_times'] = {}
times['hipster_times'] = {}

for _ in range(100):

  for itr, dictx in enumerate(test_dicts):
    start = timeit.default_timer() 
    traditional(dictx)
    end = timeit.default_timer() 
    time = end - start
    try:
      times['traditional_times'][f"dict{itr+1}"].append(time)
    except KeyError:
      times['traditional_times'][f"dict{itr+1}"] = [time]

    start = timeit.default_timer() 
    hipster(dictx)
    end = timeit.default_timer() 
    time = end - start
    try:
      times['hipster_times'][f"dict{itr+1}"].append(time)
    except KeyError:
      times['hipster_times'][f"dict{itr+1}"] = [time]

print("--d[key]--")
for x in times['traditional_times'].keys():
  ltimes = times['traditional_times'][x]
  mean = statistics.mean(ltimes)
  stdev = statistics.stdev(ltimes)
  print(f"{x} -- mean: {mean}, st. dev: {stdev}\n\n")

print("--d.items()--")
for x in times['hipster_times'].keys():
  ltimes = times['hipster_times'][x]
  mean = statistics.mean(ltimes)
  stdev = statistics.stdev(ltimes)
  print(f"{x} -- mean: {mean}, st. dev: {stdev}")

【讨论】:

    【解决方案2】:

    此代码只需要遍历字典一次即可从中检索所有内容:

    for key, value in dict.items():
    

    这段代码遍历整个字典一次,但只检索键:

    for key in dict:
        x = dict[key]
    

    然后,对于每个键,它必须再次进入字典以查找值。所以,它必须更慢。

    不过,整件事纯粹是学术性的,在现实生活中没有任何实际意义。当您的应用程序开始变得太慢时,这种缓慢是由您遍历字典的方式引起的。

    【讨论】:

      最近更新 更多