Use same collections.Counter for more classes

59 views Asked by At

I have 2 classes and I count different stuff with them. I would like to use the same collections.Counter object for the two classes, because some of the things I count are in common. What is the most pythonic way to do this?

So far I have 2 different classes using 2 separate counters:

from collections import Counter


class Shapes():
    def __init__():
        self.c = Counter(rotate=0, translate=0, operation=0)


class Tools():
    def __init__:
        self.c = Counter(hammer=0, scissor=0, operation=0)

One option would be to move the counter outside all classes, but it doesn't look so nice because it's not needed to have it global, I want to share it only between two classes.

from collections import Counter
c = Counter(rotate=0, translate=0, hammer=0)


class Shape():
    pass


class Tool():
    pass

What I would like to do is something like this, with just one counter for both classes:

A = Operations()
B = Tools()
# Counter['operation'] = 0 at this point

A.do_operation() # Do something and increase c['operation'] by one
B.do_operation() # Do something and increase c['operation'] by one

# Now Counter['operation'] = 2
2

There are 2 answers

4
SIGHUP On BEST ANSWER

You could have a class that maintains a Counter then subclass that as follows:

from collections import Counter

class BaseCounter:
    counter = Counter()
    def update(self, value):
        self.counter.update(value)

class Shape(BaseCounter):
    def __init__(self):
        ...

class Tool(BaseCounter):
    def __init__(self):
        ...

shape = Shape()
shape.update("abc")
tool = Tool()
tool.update("xyz")

# at this point, referencing either tool or shape would equivalent
for kv in tool.counter.items():
    print(*kv)

Output:

a 1
b 1
c 1
x 1
y 1
z 1

You could also use a singleton pattern like this:

from collections import Counter

class SingletonCounter(Counter):
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(SingletonCounter, cls).__new__(cls)
        return cls._instance


class Shape():
    def __init__(self):
        self._counter = SingletonCounter()
    @property
    def counter(self):
        return self._counter


class Tool():
    def __init__(self):
        self._counter = SingletonCounter()
    @property
    def counter(self):
        return self._counter
0
palao On

Another possibility is to create the Counter instance outside the classes and add it as an attribute of the Shape and Tool classes:

from collections import Counter
c = Counter(rotate=0, translate=0, hammer=0)


class Shape:
    def __init__(self, counter):
        self.c = counter


class Tool:
    def __init__(self, counter):
        self.c = counter

To avoid repeating the __init__ methods, you might create a MixIn with the __init__:

class CounterMixIn:
    def __init__(self, counter):
        self.c = counter

class Shape(CounterMixIn):
    ...

class Tool(CounterMixIn):
    ...

What you choose depends on what interface you need/want: do you want the counter to be an internal detail? Choose CtrlZ's solution. Do you want to be explicit about the counter? You could choose something in line with what I suggested.