web-dev-qa-db-ja.com

順序付きカウンターの作成

私はsuper()がどのように機能するかを読んでいます。私は偶然出会った このレシピ は、順序付きカウンターの作成方法を示しています:

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ドキュメント にも表示されます。

20
Sean

OrderedCounterは OrderedDictドキュメント の例として提供されており、メソッドをオーバーライドする必要なく機能します。

_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__()の呼び出しに変換されます。そしてthatのコードはdict.__setitem__()を呼び出します。

基本クラスを逆にすると、機能しなくなります。 MROが異なり、間違ったメソッドが呼び出されるためです。

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

Mroの詳細については、Python 2.3 documentation を参照してください。

28
RootTwo