I've written a metaclass which counts the number of instances for each of its children class.
So my meta has a dict like {classname: number_of_instances}
.
This is the implementation:
class MetaCounter(type):
_counter = {}
def __new__(cls, name, bases, dict):
cls._counter[name] = 0
return super().__new__(cls, name, bases, dict)
def __call__(cls, *args, **kwargs):
cls._counter[cls.__name__] += 1
print(f'Instantiated {cls._counter[cls.__name__]} objects of class {cls}!')
return super().__call__(*args, **kwargs)
class Foo(metaclass=MetaCounter):
pass
class Bar(metaclass=MetaCounter):
pass
x = Foo()
y = Foo()
z = Foo()
a = Bar()
b = Bar()
print(MetaCounter._counter)
# {'Foo': 3, 'Bar': 2} --> OK!
print(Foo._counter)
# {'Foo': 3, 'Bar': 2} --> I'd like it printed just "3"
print(Bar._counter)
# {'Foo': 3, 'Bar': 2} --> I'd like it printed just "2"
It works fine but I want to add a level of information hiding. That is, classes whom metaclass is MetaCounter should not have the counter of instances of other classes with the same meta. They should just have the information regarding the number of their instances.
So MetaCounter._counter
should display the entire dict, but Foo._counter
(let Foo be a class with MetaCounter as its metaclass) should just return the number of Foo instances.
Is there a way to achieve this?
I've tried to override __getattribute__
but it ended in messing around with the counting logic.
I've also tried to put _counter
as attribute in the dict
parameter of the __new__
method, but then it became also an instance member and I didn't want so.
I do not know enough metaclasses to say if it is a bad idea to do what you are trying but it is possible.
You can make
MetaCounter
just keep references to its "metaclassed" classes and add a specific instance counter to each one by updating thedict
inMetaCounter.__new__
method:Each time a new
MetaCounter
class is instantiated, you increment the counterThis ensures each
MetaCounter
class owns its instance counter and does not know anything about otherMetaCounter
classes.Then, to get all the counters, you can add a
_counters
static method toMetaCounter
, which returns the counter for eachMetaCounter
class:Then you are done: