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:
defaultdict but not for Counter? And, if so, is there a straightforward explanation?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})
                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))
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With