0

I want to create several classes, each a namedtuple of Counters. 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)

sds
  • 58,617
  • 29
  • 161
  • 278

0 Answers0