繁体   English   中英

使用元类来跟踪python中的实例

[英]Using metaclass to keep track of instances in python

我需要跟踪某些类的实例(并对这些类进行其他操作)。 我不想在所讨论的类中声明任何其他代码,因此理想情况下,应在元类中处理所有内容。

我不知道的是如何为这些类的每个新实例添加一个弱引用。 例如:

class Parallelizable(type):
    def __new__(cls, name, bases, attr):
        meta = super().__new__(cls, name, bases, attr)
        # storing the instances in this WeakSet
        meta._instances = weakref.WeakSet()
        return meta

    @property
    def instances(cls):
        return [x for x in cls._instances]

class Foo(metaclass=Parallelizable)
    def __init__(self, name):
        super().__init__()
        self.name = name

        # I would like to avoid having to do that - instead have the metaclass manage it somehow
        self._instances.add(self)

有任何想法吗? 我似乎找不到元类方面的钩子来进入Foo的__init__

当其“关联”类的每个新实例为__call__时,在元类上__call__ 如果将代码记录在其中,那么这就是您需要做的所有工作:


from weakref import WeakSet

# A convenient class-level descriptor to retrieve the instances:

class Instances:
    def __get__(self, instance, cls):
        return [x for x in cls._instances]

class Parallelizable(type):
    def __init__(cls, name, bases, attrs, **kw):
        super().__init__(name, bases, attrs, **kw)
        cls._instances = WeakSet()
        cls.instances = Instances()

    def __call__(cls, *args, **kw):
        instance = super().__call__(*args, **kw)
        cls._instances.add(instance)
        return instance

完全没有描述符的情况下,相同的代码也可以工作-这是拥有报告实例的类属性的好方法。 但是,如果WeakSet足够,则此代码就足够了:


from weakref import WeakSet
class Parallelizable(type):
    def __init__(cls, name, bases, attrs, **kw):
        super().__init__(name, bases, attrs, **kw)
        cls.instances = WeakSet()

    def __call__(cls, *args, **kw):
        instance = super().__call__(*args, **kw)
        cls.instances.add(instance)
        return instance

您可以在Parallizable.__new__装饰attrs['__init__']方法:

import weakref
import functools
class Parallelizable(type):
    def __new__(meta, name, bases, attrs):
        attrs['__init__'] = Parallelizable.register(attrs['__init__'])
        cls = super().__new__(meta, name, bases, attrs)
        cls._instances = weakref.WeakSet()
        return cls

    @classmethod
    def register(cls, method):
        @functools.wraps(method)
        def newmethod(self, *args, **kwargs):
            method(self, *args, **kwargs)
            self._instances.add(self)
        return newmethod

    @property
    def instances(cls):
        return [x for x in cls._instances]

class Foo(metaclass=Parallelizable):
    def __init__(self, name):
        "Foo.__init__ doc string"
        super().__init__()
        self.name = name

# Notice that Foo.__init__'s docstring is preserved even though the method has been decorated
help(Foo.__init__)
# Help on function __init__ in module __main__:
#
# __init__(self, name)
#     Foo.__init__ doc string

stilton = Foo('Stilton')
gruyere = Foo('Gruyere')
print([inst.name for inst in Foo.instances])
# ['Gruyere', 'Stilton']

del stilton
print([inst.name for inst in Foo.instances])
# ['Gruyere']

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM