I want to create several classes, each a namedtuple
of Counter
s.
My attempt at abstracting identical methods is:
import collections
class NamedCounters():
"Collect stats for saving to disk."
def __new__(cls):
return super().__new__(cls,**{f:collections.Counter() for f in cls._fields})
def note(self, val):
"New observation."
raise NotImplementedError("NamedCounters.note",self,val)
def as_dict(self):
"Convert to a dict for saving to file."
return {n:c.most_common() for n,c in self._asdict().items()}
@classmethod
def of_iter(cls, it):
"Collect stats on all records"
ret = cls()
for val in it:
ret.note(val)
return ret
@classmethod
def of_dict(cls, di):
"Convert from the dict read from a file."
ret = cls()
for f in cls._fields:
getattr(ret,f).update(dict(di.get(f,[])))
return ret
def f(d):
return len(d)+1
class Stat1(NamedCounters, collections.namedtuple("Stat1",["a","b"])):
def note(self, val):
self.a[val["asdf"]] += 1
self.b[f(val)] += 1
def z(d):
return len(d)+2
class Stat2(NamedCounters, collections.namedtuple("Stat2",["c","d"])):
def note(self, val):
self.c[val["qwer"]] += 1
self.d[z(val)] += 1
And it does appear to work:
>>> s1 = Stat1.of_iter([{"asdf":"q"}, {"asdf":"z"}])
>>> s1
Stat1(a=Counter({'q': 1, 'z': 1}), b=Counter({2: 2}))
>>> s1 == Stat1.of_dict(s1.as_dict())
True
>>> s2 = Stat2.of_iter([{"qwer":"d"}, {"qwer":"x"}])
>>> s2
Stat2(c=Counter({'d': 1, 'x': 1}), d=Counter({3: 2}))
>>> s2 == Stat2.of_dict(s2.as_dict())
True
What I do not like about this approach is that
NamedCounters
uses _fields
and _asdict
which come from namedtuple
.
It would be nice if NamedCounters
inherited from another ("official") class that provided those.
My main problem is that pylint
complains about NamedCounters
but not about this class Bad
which forgot to inherit from namedtuple
:
class Bad(NamedCounters):
def note(self, val):
print(val,self)
Any suggestions?
(See also Mix-in of abstract class and namedtuple and Abstract class + mixin + multiple inheritance in python)