Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are Counter / defaultdict ordered in Python 3.7?

We know in Python 3.6 dictionaries are insertion ordered as an implementation detail, and in 3.7 insertion ordering can be relied upon.

I expected this to also be the case for subclasses of dict such as collections.Counter and collections.defaultdict. But this appears to only hold true for the defaultdict case.

So my questions are:

  1. Is it true that ordering is maintained for defaultdict but not for Counter? And, if so, is there a straightforward explanation?
  2. Should ordering of these dict subclasses in the collections module be considered implementation details? Or, for example, can we rely on defaultdict being insertion ordered like dict in Python 3.7+?

Here are my rudimentary tests:

dict: ordered

words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]

dict_counter = {}
for w in words:
    dict_counter[w] = dict_counter.get(w, 0)+1

print(dict_counter)

# {'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

Counter: unordered

from collections import Counter, defaultdict

print(Counter(words))

# Counter({'apples': 3, 'kiwis': 2, 'oranges': 1, 'bananas': 1})

defaultdict: ordered

dict_dd = defaultdict(int)
for w in words:
    dict_dd[w] += 1

print(dict_dd)

# defaultdict(<class 'int'>, {'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2})
like image 351
jpp Avatar asked Sep 04 '18 21:09

jpp


1 Answers

Counter and defaultdict are both ordered now, and you can rely on it. Counter just doesn't look ordered because its repr was designed before dict ordering was guaranteed, and Counter.__repr__ sorts entries by descending order of value.

def __repr__(self):
    if not self:
        return '%s()' % self.__class__.__name__
    try:
        items = ', '.join(map('%r: %r'.__mod__, self.most_common()))
        return '%s({%s})' % (self.__class__.__name__, items)
    except TypeError:
        # handle case where values are not orderable
        return '{0}({1!r})'.format(self.__class__.__name__, dict(self))
like image 129
user2357112 supports Monica Avatar answered Sep 23 '22 10:09

user2357112 supports Monica