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

问题描述

我写了一个 metaclass ,它计算每个子类的实例数量。 所以我的 meta 有一个像{classname: number_of_instances}这样的 dict

这是实现:

class MetaCounter(type):
    
    _counter = {}

    def __new__(cls,name,bases,dict):
        cls._counter[name] = 0
        return super().__new__(cls,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 的类不应具有具有相同元数据的其他类的实例的计数器。他们应该只具有有关实例数量的信息。 因此MetaCounter._counter应该显示整个 dict ,但是Foo._counter(让 Foo 是一个以 MetaCounter 作为其 metaclass )应该只返回 Foo 实例的数量。

有没有办法做到这一点?

我尝试覆盖__getattribute__,但是最终弄乱了计数逻辑。

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

解决方法

我不知道足够多的元类来说明做您尝试的事情是否是个坏主意,但是有可能。

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

class MetaCounter(type):
    _classes = []

    def __new__(cls,name,bases,dict):
        dict.update(_counter=0)
        metaclassed = super().__new__(cls,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
,

这是一个有效的示例,其中使用描述符来根据{em> instance 是否访问了_counter属性(该类)或 class (在这种情况下为元类)

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,value):
        self.counter[instance] = value
    

class MetaCounter(type):
    
    _counter = descr()

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


    def __call__(cls,**kwargs)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...