39 votes

Obtenir le nombre de clés dans un dictionnaire de dictionnaires en Python

J'ai un dictionnaire de dictionnaires en Python 2.7.

Je dois compter rapidement le nombre de toutes les clés, y compris les clés de chacun des dictionnaires.

Donc, dans cet exemple, il faudrait que le nombre de toutes les clés soit égal à 6:

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

Je sais que je peux parcourir chaque clé avec des boucles for, mais je cherche un moyen plus rapide de le faire, car j'aurai des milliers / millions de clés, ce qui est tout simplement inefficace:

 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())
 

31voto

donkopotamus Points 4644

Garder les choses Simples

Si nous savons que toutes les valeurs sont les dictionnaires, et ne souhaitez pas vérifier que leurs valeurs sont aussi les dictionnaires, alors c'est aussi simple que:

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

De l'affiner un peu, pour vérifier que les valeurs sont les dictionnaires avant de les compter:

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

Et enfin, si vous souhaitez faire une profondeur arbitraire, quelque chose comme ce qui suit:

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

Dans ce dernier cas, on définit une fonction qui sera appelée récursivement. Compte tenu d'une valeur d, nous retourner soit:

  • 0 si valeur n'est pas un dictionnaire; ou
  • le nombre de clés dans le dictionnaire, plus le total des clés dans l'ensemble de nos enfants.

Le rendant plus Rapide

Le dessus est un succincte et facile à comprendre l'approche. Nous pouvons obtenir un peu plus vite à l'aide d'un générateur:

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))

Cela nous met un peu plus de performance:

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

9voto

MaxBenChrist Points 139

Que diriez -

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

Ce que vous faites est une itération sur toutes les touches k et les valeurs de c. Les valeurs de v sont vos subdictionaries. Vous obtenez la longueur de ces dictionnaires et ajoutez un à inclure la clé utilisée pour l'indice de la subdictionary.

Ensuite, vous somme sur la liste pour obtenir le nombre de touches.

EDIT:

Pour clarifier, ce modèle fonctionne uniquement pour les dictionnaires les dictionnaires comme l'a demandé. Pas de dictionnaire des dictionnaires de dictionnaires...
Donc, ne pas l'utiliser pour imbriquée exemple :)

9voto

Kasramvd Points 32864

De manière plus générale, vous pouvez utiliser une fonction de récurrence et une expression génératrice:

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

Exemple:

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

Remarque : En python 3.X, utilisez la méthode dict.items() au lieu de iteritems() .

Un repère avec une réponse acceptée qui montre que cette fonction est plus rapide que la réponse acceptée:

 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'}}")
 

résultat:

 1st:  4.65556812286
2nd :  4.09120802879
 

6voto

user3100115 Points 15249

Utilisation d'une fonction de générateur et de la syntaxe yield from nouvelle dans Python 3.x. Cela fonctionnera pour un dictionnaire imbriqué arbitraire

 >>> 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
 

Dans Python 2.x, vous avez besoin de cela pour:

 >>> 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
 

5voto

viakondratiuk Points 1847

Quelque chose comme:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X