For example, I need to count how many times a word appears in a list, not sorted by frequency but with the order in which the words appear, i.e. insertion order.
<
In Python 3.6, dictionaries are insertion ordered, but this is an implementation detail.
In Python 3.7+, insertion order is guaranteed and can be relied upon. See Are dictionaries ordered in Python 3.6+? for more details.
So, depending on your Python version, you may wish to just use Counter
as is, without creating an OrderedCounter
class as described in the documentation. This works because Counter
is a subclass of dict
, i.e. issubclass(Counter, dict)
returns True
, and therefore inherits the insertion ordering behaviour of dict
.
String representation
It is worth noting the the string representation for Counter
, as defined in the repr
method, has not been updated to reflect the change in 3.6 / 3.7, i.e. print(Counter(some_iterable))
still returns items from largest counts descending. You can trivially return the insertion order via list(Counter(some_iterable))
.
Here are some examples demonstrating the behaviour:
x = 'xyyxy'
print(Counter(x)) # Counter({'y': 3, 'x': 2}), i.e. most common first
print(list(Counter(x))) # ['x', 'y'], i.e. insertion ordered
print(OrderedCounter(x)) # OC(OD([('x', 2), ('y', 3)])), i.e. insertion ordered
Exceptions
You should not use a regular Counter
if additional or overwritten methods available to OrderedCounter
are important to you. Of particular note:
OrderedDict
and consequently OrderedCounter
offer popitem and move_to_end methods.OrderedCounter
objects are order-sensitive and are implemented as list(oc1.items()) == list(oc2.items())
.For example, equality tests will yield different results:
Counter('xy') == Counter('yx') # True
OrderedCounter('xy') == OrderedCounter('yx') # False