计算Python中使用元类的实例数

2024-10-02 16:27:01 发布

您现在位置:Python中文网/ 问答频道 /正文

我编写了一个元类,它统计每个子类的实例数。 所以我的meta有一个dict类似{classname: number_of_instances}

这就是实现:

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"

它工作得很好,但我想增加一个信息隐藏级别。也就是说,元类元计数器的类不应该具有具有相同元计数器的其他类的实例计数器。他们应该只知道有关其实例数量的信息。 因此MetaCounter._counter应该显示整个dict,但是Foo._counter(让Foo是一个类,其元类使用元计数器)应该只返回Foo实例的数量

有没有办法做到这一点

我试图重写__getattribute__,但结果是弄乱了计数逻辑

我还尝试将_counter作为属性放在__new__方法的dict参数中,但后来它也变成了实例成员,我不想这样做


Tags: of实例namenewfoodefcounter计数器
2条回答

我不知道足够的元类来说明做你正在尝试的事情是否是个坏主意,但这是可能的

您可以使MetaCounter只保留对其“元类化”类的引用,并通过更新MetaCounter.__new__方法中的dict为每个类添加特定的实例计数器:

class MetaCounter(type):
    _classes = []

    def __new__(cls, name, bases, dict):
        dict.update(_counter=0)
        metaclassed = super().__new__(cls, name, bases, dict)
        cls._classes.append(metaclassed)
        return metaclassed

每次实例化一个新的MetaCounter类时,都会递增计数器

    def __call__(cls, *args, **kwargs):
        cls._counter += 1
        print(f'Instantiated {cls._counter} objects of class {cls}!')
        return super().__call__(*args, **kwargs)

这确保了每个MetaCounter类都拥有自己的实例计数器,而不知道其他MetaCounter类的任何信息

然后,要获取所有计数器,可以将_counters静态方法添加到MetaCounter,该方法返回每个MetaCounter类的计数器:

    @classmethod
    def _counters(cls):
        return {klass: klass._counter for klass in cls._classes}

那么你就完成了:

print(MetaCounter._counters())  # {<class '__main__.Foo'>: 3, <class '__main__.Bar'>: 2}
print(Foo._counter)  # 3
print(Bar._counter)  # 2

这是一个工作示例,使用描述符来修改_counter属性的行为,基于它是由实例(类)访问还是由(本例中的元类)访问

class descr:

    def __init__(self):
        self.counter = {}
        self.previous_counter_value = {}

    def __get__(self, instance, cls):
        if instance:
            return self.counter[instance]
        else:
            return self.counter

    def __set__(self, instance, value):
        self.counter[instance] = value
    

class MetaCounter(type):
    
    _counter = descr()

    def __new__(cls, name, bases, dict):
        new_class = super().__new__(cls, name, bases, dict)
        cls._counter[new_class] = 0
        return new_class


    def __call__(cls, *args, **kwargs):
        cls._counter += 1
        print(f'Instantiated {cls._counter} objects of class {cls}!')
        return super().__call__(*args, **kwargs)

相关问题 更多 >