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),)
from collections import OrderedDict, Callable
class DefaultOrderedDict(OrderedDict):
# Source: http://stackoverflow.com/a/6190500/562769
def __init__(self, default_factory=None, *a, **kw):
if (default_factory is not None and
not isinstance(default_factory, Callable)):
raise TypeError('first argument must be callable')
OrderedDict.__init__(self, *a, **kw)
self.default_factory = default_factory
def __getitem__(self, key):
try:
return OrderedDict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)
self[key] = value = self.default_factory()
return value
def __reduce__(self):
if self.default_factory is None:
args = tuple()
else:
args = self.default_factory,
return type(self), args, None, None, self.items()
def copy(self):
return self.__copy__()
def __copy__(self):
return type(self)(self.default_factory, self)
def __deepcopy__(self, memo):
import copy
return type(self)(self.default_factory,
copy.deepcopy(self.items()))
def __repr__(self):
return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
OrderedDict.__repr__(self))
I recently wanted to use an OrderedCounter and an OrderedDefaultDict. After a bit of googling I discovered that OrderedCounter is quite easy to implement:
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),)
from https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
Unfortunately an OrderedDefaultDict did not seem so easy. I did find http://stackoverflow.com/questions/6190331/can-i-do-an-ordered-default-dict-in-python which suggests:
On Oct 16, 2015, at 03:36, Ian Foote <i...@feete.org> wrote:I recently wanted to use an OrderedCounter and an OrderedDefaultDict. After a bit of googling I discovered that OrderedCounter is quite easy to implement:This is already in the docs for OrderedDict, so it shouldn't have taken any googling.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),)
from https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
Unfortunately an OrderedDefaultDict did not seem so easy. I did find http://stackoverflow.com/questions/6190331/can-i-do-an-ordered-default-dict-in-python which suggests:Most of the trickiness here is handling None as a factory, which is only useful for subclasses that implement a custom __missing__
To make something that works like you'd expect except for that part is a lot simpler.
Actually, forget all that; it's even simpler.
At least in recent 3.x, the only thing wrong with inheriting from both types, assuming you put OrderedDict first, is the __init__ signature. So:
class OrderedDefaultDict(OrderedDict, defaultdict):def __init__(self, default_factory=None, *a, **kw):OrderedDict.__init__(self, *a, **kw)self.default_factory = default_factory
On Wed, Oct 21, 2015 at 09:50:21AM +1100, Chris Angelico wrote:
> To me, this is the biggest benefit of inheritance: you do NOT have to
> predict what someone might want to do. I can subclass someone else's
> object and change how it works.
I think this is wrong. I think that in general you need to have a fairly
good idea of the internal workings of the class before you can inherit
from it. Otherwise, how do you know what methods need to be overwritten?
She recommends a massive superclass that's capable of any form of injection
On Oct 21, 2015, at 11:25, Jonathan Slenders <jona...@slenders.be> wrote:Just want to say that I'm happy to see that lately the disadvantages of inheritance (which are already known for a very long time) are getting more attention. It's not bad by definition, but there's so much truth in Sandy her talk and I think for many Python projects, we went way too far into "abusing" inheritance. Actually, it's a bit unfortunate that we made inheritance so user friendly and powerful in Python that for many people it became the logical way to extend or reuse some code.One of the most important things people have learned about OO over the past two decades is that subtyping, implementation extension, and implementation mixins, and interface-extending mixins (think collections.abc.Sequence adding count for you) are all different things. Compared to the other languages in existence at the time of Python 1.x or even the 2.2/2.3 changeover, it's hard to fault Python. The fact that it can easily be used to write bad code is a little unfortunate, but the fact that it can also easily be used to write good code, when other languages either don't allow some things to be expressed, force them to be expressed in clumsy or limited ways, or force you to misuse inappropriate features instead more than makes up for it. In particular, any language that has fixed structure layouts and vtables makes is much more unfriendly to both kinds of mixins, and makes interface subtyping clumsy; Python has no such problems. Yeah, maybe we could design something better in 2015, b ut based on the knowledge people had at the time?
On 10/21/2015 1:41 PM, Sven R. Kunze wrote:
The "superclass" is not "massive" at all. It is even slimmer as
orthogonal aspects are refactored out into separate entities. In fact,
it makes it even easier to test and maintain these separate aspects (the
core dev should be interested in that). Furthermore, it's, of course, up
to debate which aspects should be injectable and which are not.
The dict class itself is, in a sense, a poor example for this discussion. It is a critical part of Python's infrastructure, involved in a large fraction of executed statements. It therefore needs to be as fast as possible. For CPython, this means a heavily optimized C implementation that disallows injection and that takes shortcuts like bypassing method lookups. This makes the usefulness of subclassing limited.
Actually, forget all that; it's even simpler.At least in recent 3.x, the only thing wrong with inheriting from both types, assuming you put OrderedDict first, is the __init__ signature. So:class OrderedDefaultDict(OrderedDict, defaultdict):def __init__(self, default_factory=None, *a, **kw):OrderedDict.__init__(self, *a, **kw)self.default_factory = default_factoryMore importantly, because __missing__ support is built into dict, despite the confusing docs for defaultdict, you don't really need defaultdict at all here:class OrderedDefaultDict(OrderedDict):def __init__(self, default_factory=None, *a, **kw):OrderedDict.__init__(self, *a, **kw)self.default_factory = default_factorydef __missing__(self, key):self[key] = value = default_factory()return valueAnd either of these should work with 2.5+ (according to https://docs.python.org/2/library/stdtypes.html#dict that's when dict.__missing__ was added).
On Fri, Oct 16, 2015 at 9:08 PM, Andrew Barnert via Python-ideas <python...@python.org> wrote:Actually, forget all that; it's even simpler.At least in recent 3.x, the only thing wrong with inheriting from both types, assuming you put OrderedDict first, is the __init__ signature. So:class OrderedDefaultDict(OrderedDict, defaultdict):def __init__(self, default_factory=None, *a, **kw):OrderedDict.__init__(self, *a, **kw)self.default_factory = default_factoryMore importantly, because __missing__ support is built into dict, despite the confusing docs for defaultdict, you don't really need defaultdict at all here:class OrderedDefaultDict(OrderedDict):def __init__(self, default_factory=None, *a, **kw):OrderedDict.__init__(self, *a, **kw)self.default_factory = default_factorydef __missing__(self, key):self[key] = value = default_factory()return valueAnd either of these should work with 2.5+ (according to https://docs.python.org/2/library/stdtypes.html#dict that's when dict.__missing__ was added).Thanks!- [ ] Could/should maybe either of these make it into the standard library,that would save a fair amount of copying.
.. Great in combination w/ dict views: