创建有序计数器

时间:2016-02-17 00:59:10

标签: python python-3.x dictionary counter ordereddictionary

我一直在阅读super()的工作原理。我遇到了this recipe,它演示了如何创建有序计数器:

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first seen'
     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__,
                            OrderedDict(self))
     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

例如:

oc = OrderedCounter('adddddbracadabra')

print(oc)

OrderedCounter(OrderedDict([('a', 5), ('d', 6), ('b', 2), ('r', 2), ('c', 1)]))

有人能够解释这是如何神奇地起作用吗?

这也出现在Python documentation

2 个答案:

答案 0 :(得分:23)

OrderedCounter在OrderedDict documentation中作为示例提供,无需覆盖任何方法即可运行:

class OrderedCounter(Counter, OrderedDict):
    pass

调用类方法时,Python必须找到要执行的正确方法。有一个定义的顺序,它搜索类层次结构,称为"方法解析顺序"或者mro。 mro存储在属性__mro__

OrderedCounter.__mro__

(<class '__main__.OrderedCounter'>, <class 'collections.Counter'>, <class 'collections.OrderedDict'>, <class 'dict'>, <class 'object'>)

当OrderedDict的实例正在调用__setitem__()时,它会按顺序搜索这些类:OrderedCounterCounterOrderedDict(找到它的位置)。因此,像oc['a'] = 0这样的声明最终会调用OrderedDict.__setitem__()

相反,__getitem__不会被mro中的任何子类覆盖,因此count = oc['a']dict.__getitem__()处理。

oc = OrderedCounter()    
oc['a'] = 1             # this call uses OrderedDict.__setitem__
count = oc['a']         # this call uses dict.__getitem__

对于像oc.update('foobar').之类的语句,会发生一个更有趣的调用序列。首先,Counter.update()被调用。 Counter.update()的代码使用self [elem],后者变为对OrderedDict.__setitem__()的调用。 的代码调用dict.__setitem__()

如果基类反转,则不再有效。因为mro不同而且调用了错误的方法。

class OrderedCounter(OrderedDict, Counter):   # <<<== doesn't work
    pass

有关mro的更多信息,请参阅Python 2.3 documentation

答案 1 :(得分:1)

我认为当单词作为输入时,我们需要在类中表示这些方法 reprreduce

没有 reprreduce

from collections import Counter, OrderedDict
class OrderedCounter(Counter, OrderedDict):
    pass

oc = OrderedCounter(['apple', 'banana', 'cherry', 'mango', 'apple', 'pie', 'mango'])
print(oc)

输出:

OrderedCounter({'apple': 2, 'mango': 2, 'banana': 1, 'cherry': 1, 'pie': 1})

上例中的顺序没有保留。

使用 reprreduce

from collections import Counter, OrderedDict
class OrderedCounter(Counter, OrderedDict):
    'Counter that remembers the order elements are first encountered'
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)
oc = OrderedCounter(['apple', 'banana', 'cherry', 'mango', 'apple', 'pie', 'mango'])
print(oc)

输出:

OrderedCounter(OrderedDict([('apple', 2), ('banana', 1), ('cherry', 1), ('mango', 2), ('pie', 1)]))