猿问

在 Python 中使用元类计​​算实例数量

我编写了一个元类,它计算每个子类的实例数。所以我的元有一个类似的字典{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的类不应该具有具有相同元的其他类的实例的计数器。他们应该只知道有关其实例数量的信息。因此应该显示整个dict,但是(让Foo是一个以MetaCounter作为其元类的类)应该只返回Foo实例的数量。MetaCounter._counterFoo._counter


有办法实现这一点吗?


我尝试过覆盖__getattribute__,但最终导致计数逻辑混乱。


我还尝试将其_counter作为属性放入方法dict的参数中__new__,但随后它也成为实例成员,而我不希望如此。


红颜莎娜
浏览 186回答 2
2回答

湖上湖

我不知道足够的元类来判断做你正在尝试的事情是否是一个坏主意,但这是可能的。您可以MetaCounter仅保留对其“元类”类的引用,并通过更新dictinMetaCounter.__new__方法为每个类添加特定的实例计数器:class MetaCounter(type):&nbsp; &nbsp; _classes = []&nbsp; &nbsp; def __new__(cls, name, bases, dict):&nbsp; &nbsp; &nbsp; &nbsp; dict.update(_counter=0)&nbsp; &nbsp; &nbsp; &nbsp; metaclassed = super().__new__(cls, name, bases, dict)&nbsp; &nbsp; &nbsp; &nbsp; cls._classes.append(metaclassed)&nbsp; &nbsp; &nbsp; &nbsp; return metaclassed每次MetaCounter实例化一个新类时,都会增加计数器&nbsp; &nbsp; def __call__(cls, *args, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; cls._counter += 1&nbsp; &nbsp; &nbsp; &nbsp; print(f'Instantiated {cls._counter} objects of class {cls}!')&nbsp; &nbsp; &nbsp; &nbsp; return super().__call__(*args, **kwargs)这确保每个MetaCounter类拥有其实例计数器并且不知道其他MetaCounter类的任何信息。然后,要获取所有计数器,您可以_counters向 中添加一个静态方法MetaCounter,该方法返回每个类的计数器MetaCounter:&nbsp; &nbsp; @classmethod&nbsp; &nbsp; def _counters(cls):&nbsp; &nbsp; &nbsp; &nbsp; return {klass: klass._counter for klass in cls._classes}然后你就完成了:print(MetaCounter._counters())&nbsp; # {<class '__main__.Foo'>: 3, <class '__main__.Bar'>: 2}print(Foo._counter)&nbsp; # 3print(Bar._counter)&nbsp; # 2

红糖糍粑

这是一个工作示例,使用描述符根据属性是由实例(类)_counter访问还是由类(本例中为元类)访问来修改属性的行为class descr:&nbsp; &nbsp; def __init__(self):&nbsp; &nbsp; &nbsp; &nbsp; self.counter = {}&nbsp; &nbsp; &nbsp; &nbsp; self.previous_counter_value = {}&nbsp; &nbsp; def __get__(self, instance, cls):&nbsp; &nbsp; &nbsp; &nbsp; if instance:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return self.counter[instance]&nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return self.counter&nbsp; &nbsp; def __set__(self, instance, value):&nbsp; &nbsp; &nbsp; &nbsp; self.counter[instance] = value&nbsp; &nbsp;&nbsp;class MetaCounter(type):&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; _counter = descr()&nbsp; &nbsp; def __new__(cls, name, bases, dict):&nbsp; &nbsp; &nbsp; &nbsp; new_class = super().__new__(cls, name, bases, dict)&nbsp; &nbsp; &nbsp; &nbsp; cls._counter[new_class] = 0&nbsp; &nbsp; &nbsp; &nbsp; return new_class&nbsp; &nbsp; def __call__(cls, *args, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; cls._counter += 1&nbsp; &nbsp; &nbsp; &nbsp; print(f'Instantiated {cls._counter} objects of class {cls}!')&nbsp; &nbsp; &nbsp; &nbsp; return super().__call__(*args, **kwargs)
随时随地看视频慕课网APP

相关分类

Python
我要回答