Keeping it Simple
If we know all the values are dictionaries, and do not wish to check that any of their values are also dictionaries, then it is as simple as:
len(dict_test) + sum(len(v) for v in dict_test.itervalues())
Refining it a little, to actually check that the values are dictionaries before counting them:
len(dict_test) + sum(len(v) for v in dict_test.itervalues() if isinstance(v, dict))
And finally, if you wish to do an arbitrary depth, something like the following:
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
In this last case, we define a function that will be called recursively. Given a value d
, we return either:
0
if that value is not a dictionary; or
- the number of keys in the dictionary, plus the total of keys in all of our children.
Making it Faster
The above is a succinct and easily understood approach. We can get a little faster using a generator:
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))
This gets us a bit more 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
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…