Suppose I define a custom list-type:
class MyList(list):
def __reversed__(self):
print 'called MyList.__reversed__'
return super(MyList, self).__reversed__()
def count(self, item):
print 'called MyList.count'
return super(MyList, self).count(item)
All this does is indicate when either __reversed__
or count
is called.
Now suppose I define another type with a MyList
member variable. I'd like a couple of the methods on my new type to dispatch to the MyList
methods so I set the methods in __init__
like so:
class GoodList:
def __init__(self, iterable):
self._list = MyList(iterable)
self.__reversed__ = self._list.__reversed__
self.count = self._list.count
def __getitem__(self, index):
return 0
def __len__(self):
return 0
Later I decide that GoodList
should inherit from collections.Sequence
. But when I do so, the behavior changes. As an example, I'll define BadList
, the only difference being the inheritance:
from collections import Sequence
class BadList(Sequence):
def __init__(self, iterable):
self._list = MyList(iterable)
self.__reversed__ = self._list.__reversed__
self.count = self._list.count
def __getitem__(self, index):
return 0
def __len__(self):
return 0
Now when I create a GoodList
and call __reversed__
and count
, I get the messages I expect, printed from MyList
. Here's the code to exercise it:
print 'GOOD ' + '*' * 75
good = GoodList([1, 2, 3, 4, 5])
print 'GOOD list(reversed(good))'
print list(reversed(good))
print 'GOOD good.count(3)'
print good.count(3)
And here's the output:
GOOD ***************************************************************************
GOOD list(reversed(good))
called MyList.__reversed__
[5, 4, 3, 2, 1]
GOOD good.count(3)
called MyList.count
1
Instead, when I create a BadList
and call __reversed__
and count
, I get the message I expect only for count
. The __reversed__
method is dispatched to the inherited Sequence.__reversed__
method. Here's the code to exercise it:
print 'BAD ' + '*' * 76
bad = BadList([1, 2, 3, 4, 5])
print 'BAD list(reversed(bad))'
print list(reversed(bad))
print 'BAD bad.count(3)'
print bad.count(3)
And here's the output:
BAD ****************************************************************************
BAD list(reversed(bad))
[]
BAD bad.count(3)
called MyList.count
1
Why does method dispatch appear to work differently for double-under (dunder) methods? How can I inherit from Sequence
but still make __reversed__
dispatch to _list.__reversed__
?