【问题标题】:Get the number of all keys in a dictionary of dictionaries in Python获取Python中字典字典中所有键的数量
【发布时间】:2016-05-27 10:50:59
【问题描述】:

我有一本 Python 2.7 字典。

我需要快速计算所有键的数量,包括每个字典中的键。

所以在这个例子中,我需要所有键的数量为 6:

dict_test = {'key2': {'key_in3': 'value', 'key_in4': 'value'}, 'key1': {'key_in2': 'value', 'key_in1': 'value'}}

我知道我可以使用 for 循环遍历每个键,但我正在寻找一种更快的方法来执行此操作,因为我将拥有数千/数百万个键并且这样做是无效的:

count_the_keys = 0

for key in dict_test.keys():
    for key_inner in dict_test[key].keys():
       count_the_keys += 1

# something like this would be more effective
# of course .keys().keys() doesn't work
print len(dict_test.keys()) * len(dict_test.keys().keys())

【问题讨论】:

  • 虽然你不要求这个,但如果你想要 distinct 键的数量,那么你可以做类似len(set(itertools.chain(dict_test, *dict_test.values())))

标签: python python-2.7 dictionary


【解决方案1】:

作为一种更通用的方式,您可以使用递归函数和生成器表达式:

>>> def count_keys(dict_test):
...     return sum(1+count_keys(v) if isinstance(v,dict) else 1 for _,v in dict_test.iteritems())
... 

例子:

>>> dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}
>>> 
>>> count(dict_test)
8

注意:在 python 3.X 中使用dict.items() 方法而不是iteritems()

具有公认答案的基准测试表明此功能比公认答案更快:

from timeit import timeit

s1 = """
def sum_keys(d):
    return 0 if not isinstance(d, dict) else len(d) + sum(sum_keys(v) for v in d.itervalues())

sum_keys(dict_test)
"""

s2 = """
def count_keys(dict_test):
    return sum(1+count_keys(v) if isinstance(v,dict) else 1 for _,v in dict_test.iteritems())

count_keys(dict_test)
   """

print '1st: ', timeit(stmt=s1,
                      number=1000000,
                      setup="dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}")
print '2nd : ', timeit(stmt=s2,
                       number=1000000,
                       setup="dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}")

结果:

1st:  4.65556812286
2nd :  4.09120802879

【讨论】:

  • 这也失败了:dict_test = { "a": { "b": "1", "c": "2", "d": "3", "e": {"f": 1} }}
  • @Idos 是的,它适用于 2 级类型。
  • @Idos OP 只询问字典字典而不是任意嵌套。
  • @Idos 查看一般答案。
【解决方案2】:

保持简单

如果我们知道所有值都是字典,并且不希望检查它们的任何值是否也是字典,那么它很简单:

len(dict_test) + sum(len(v) for v in dict_test.itervalues())

稍微改进一下,在计算值之前实际检查值是否为字典:

len(dict_test) + sum(len(v) for v in dict_test.itervalues() if isinstance(v, dict))

最后,如果您希望做任意深度,如下所示:

def sum_keys(d):
    return (0 if not isinstance(d, dict) 
            else len(d) + sum(sum_keys(v) for v in d.itervalues())

print sum_keys({'key2': {'key_in3': 'value', 'key_in4': 'value'}, 
                'key1': {'key_in2': 'value', 
                         'key_in1': dict(a=2)}})
# => 7

在最后一种情况下,我们定义了一个递归调用的函数。给定一个值d,我们返回:

  • 0 如果该值不是字典;或
  • 字典中的键数,加上我们所有孩子的键总数。

让它更快

以上是一种简洁易懂的方法。我们可以使用生成器来加快速度:

def _counter(d):
    # how many keys do we have?
    yield len(d)

    # stream the key counts of our children
    for v in d.itervalues():
        if isinstance(v, dict):
            for x in _counter(v):
                yield x

def count_faster(d):
    return sum(_counter(d))

这让我们获得了更多的性能:

In [1]: %timeit sum_keys(dict_test)
100000 loops, best of 3: 4.12 µs per loop

In [2]: %timeit count_faster(dict_test)
100000 loops, best of 3: 3.29 µs per loop

【讨论】:

  • 谢谢,这很有帮助。尤其是性能方面。
【解决方案3】:

使用生成器函数和 Python 3.x 中新增的 yield from 语法。这将适用于任意嵌套字典

>>> from collections import Mapping
>>> def count_keys(mydict):
...     for key, value in mydict.items():
...         if isinstance(value, Mapping):
...             yield from count_keys(value)
...     yield len(mydict)
... 
>>> dict_test = {'key2': {'key_in3': 'value', 'key_in4': 'value'}, 'key1': {'key_in2': 'value', 'key_in1': 'value'}}
>>> sum(count_keys(dict_test))
6

在 Python 2.x 中,您需要执行此操作:

>>> def count_keys(mydict):
...     for key, value in mydict.items():
...         if isinstance(value, Mapping):
...             for item in count_keys(value):
...                 yield 1
...         yield 1
... 
>>> sum(count_keys(dict_test))
6

【讨论】:

    【解决方案4】:

    这是查找嵌套字典的总键数的递归函数...

    s=0
    def recurse(v):
       if type(v)==type({}):
         for k in v.keys():
          global s
          s+=1
          recurse(v[k])
    

    【讨论】:

      【解决方案5】:

      len(dict) 将返回字典中键的数量,因此,假设您知道它的嵌套程度并且所有值都是字典:

      counter = len(outer_dict)
      for v in outer_dict.values :
          counter += len(v)
      

      您可以将其包装在列表理解中:

      counter = len(outer_dict)
      counter += sum([len(inner_dict) for inner_dict in outer_dict.values])
      

      这可能是最 Pythonic 的。您可以将其扩展为:

      counter = len(outer_dict)
      counter += sum([len(inner_dict) if isinstance(inner_dict, dict) else 0 for inner_dict in outer_dict.values])
      

      但我倾向于认为这是相当难以理解的。

      【讨论】:

        【解决方案6】:

        递归函数:

        def count_keys(some_dict):
            count = 0
            for key in some_dict:
                if isinstance(some_dict[key], dict):
                    count += count_keys(some_dict[key])
                count += 1
            return count
        

        【讨论】:

          【解决方案7】:

          怎么样

          n = sum([len(v)+1 for k, v in dict_test.items()])
          

          您正在做的是遍历所有键 k 和值 v。值 v 是您的子字典。您获取这些字典的长度并添加一个以包含用于索引子字典的键。

          然后你对列表求和以获得完整的键数。

          编辑:

          为了澄清,这个 sn-p 仅适用于所要求的字典的字典。不是字典的字典...
          所以不要将它用于嵌套示例:)

          【讨论】:

          • 这不包括“父”键。
          • 这就是为什么我在每个长度上加一个
          • 这是一本字典的字典。 ivan_bilan 要求提供字典中的字典...
          • @MarounMaroun:​​​​​​​​​​​​​​​等等,OP没有提到更多嵌套示例,对吧?如果dict是这样的,OP的代码也会失败。
          • @KevinGuan 如果是这样,那么这个答案是正确的。
          【解决方案8】:

          类似:

          print len(dict_test) + sum(len(v) for v in dict_test.values())

          【讨论】:

          • 不适用于更多嵌套示例,试试这个 - {1: {1: {1: {1: 2}}, 2: 3}, 2: {1: 2, 2: 3}}
          • @AlokThakur 应该吗?我的意思是它没有被要求。
          • 我上面提到的字典打印 6,你得到不同的结果吗?
          • @AlokThakur 问题只有一层,没有提到嵌套。阅读它。
          • @AlokThakur 我正在写这些 cmets,因为这个答案 100% 满足 OP 的要求,而您的 cmets 无关紧要。如果您对如何实现更多级别有疑问,请提出。我会说你需要递归调用。
          【解决方案9】:

          您可以尝试使用 pandas DataFrame:

          >>> import pandas as pd
          >>> data = {'1': {'2': 'a', '3': 'b'}, '4': {'5': 'c', '6': 'd'}, '7': {'5': 'x'}}
          >>> df = pd.DataFrame(data)
          >>> print (df.count().sum() + len(df.columns))  # 8
          

          pd.DataFrame(data) 行会将您的字典转换为 N x M 矩阵,其中 N 是“父”键的数量,M 是唯一子键的数量:

               1    4    7
          2    a  NaN  NaN
          3    b  NaN  NaN
          5  NaN    c    x
          6  NaN    d  NaN
          

          对于每个 [行、列],您都有一个值或 NaN。您只需要计算非NaN 值,这将为您提供子键的数量并添加len(df.columns),它代表列数(即父键)。

          【讨论】:

          • 这会不会是低效/内存密集型,因为 OP 说字典可能有数千/数百万个条目?我认为“空”值(其中会有很多)可能会消耗大量浪费的内存,但我不完全确定 pandas 是如何存储这些的。
          • @Matthew 肯定会创建一个大矩阵。我也对这个问题的答案感兴趣。
          • OP 没有提到内存,只提到了处理速度;)
          【解决方案10】:

          试试这个,

          l = len(dict_test)
          for k in dict_test:
              l += len(dict_test[k])
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-12-18
            • 2019-08-09
            • 2012-05-21
            • 1970-01-01
            • 2020-02-06
            • 1970-01-01
            • 2018-03-27
            相关资源
            最近更新 更多