如何跟踪类的实例?(How to keep track of class instances?)

2019-06-17 20:31发布

朝节目的最后,我希望从一个类的所有实例加载特定的变量到字典中。

例如:

class Foo():
    __init__(self):
    x = {}

foo1 = Foo()
foo2 = Foo()
foo...etc.

比方说,实例的数量会有所不同,我想从富的每个实例()加载到新字典的X字典。 我会怎么做呢?

我已经看到的例子SO假设一个已经有实例的列表。

Answer 1:

跟踪情况的一种方法是使用一个类变量:

class A(object):
    instances = []

    def __init__(self, foo):
        self.foo = foo
        A.instances.append(self)

在节目的最后,你可以创建你的字典是这样的:

foo_vars = {id(instance): instance.foo for instance in A.instances}

只有一个列表:

>>> a = A(1)
>>> b = A(2)
>>> A.instances
[<__main__.A object at 0x1004d44d0>, <__main__.A object at 0x1004d4510>]
>>> id(A.instances)
4299683456
>>> id(a.instances)
4299683456    
>>> id(b.instances)
4299683456    


Answer 2:

@ JoelCornett的回答涵盖了基础知识完美。 这是一个稍微复杂的版本,这可能与一些微妙的问题有所帮助。

如果你希望能够访问给定类的所有“活”的情况下,子类以下(或在你自己的基类等效代码):

from weakref import WeakSet

class base(object):
    def __new__(cls, *args, **kwargs):
        instance = object.__new__(cls, *args, **kwargs)
        if "instances" not in cls.__dict__:
            cls.instances = WeakSet()
        cls.instances.add(instance)
        return instance

这解决了与更简单的实现是@JoelCornett提出了两种可能的问题:

  1. 的每个子类base将分别跟踪它自己的实例。 在父类的实例列表,你不会得到子类的实例,以及一个子类永远不会绊倒同级子类的实例。 这可能是不可取的,这取决于你的使用情况,但比它要他们分开拆分它可能更容易合并集合到一起。

  2. instances设置使用的类的实例的弱引用,因此,如果您del或重新分配到所有在你的代码的其他地方实例的其他文献,簿记代码不会阻止它被垃圾收集。 同样,这可能是不希望对某些使用情况,但它是很容易的使用常规的集合(或列表),而不是weakset如果你真的想每个实例永远持续下去。

一些方便,花花公子测试输出(与instances集总是被传递给list ,只是因为他们没有打印出精美的):

>>> b = base()
>>> list(base.instances)
[<__main__.base object at 0x00000000026067F0>]
>>> class foo(base):
...     pass
... 
>>> f = foo()
>>> list(foo.instances)
[<__main__.foo object at 0x0000000002606898>]
>>> list(base.instances)
[<__main__.base object at 0x00000000026067F0>]
>>> del f
>>> list(foo.instances)
[]


Answer 3:

你可能会想使用弱引用您的实例。 否则,类可以有可能最终跟踪是注定要被删除的实例。 一个weakref.WeakSet将自动从其设定删除任何死亡情况。

跟踪情况的一种方法是使用一个类变量:

import weakref
class A(object):
    instances = weakref.WeakSet()

    def __init__(self, foo):
        self.foo = foo
        A.instances.add(self)

    @classmethod
    def get_instances(cls):
        return list(A.instances) #Returns list of all current instances

在节目的最后,你可以创建你的字典是这样的:

foo_vars = {ID(实例):instance.foo例如在A.instances}只有一个清单:

>>> a = A(1)
>>> b = A(2)
>>> A.get_instances()
[<inst.A object at 0x100587290>, <inst.A object at 0x100587250>]
>>> id(A.instances)
4299861712
>>> id(a.instances)
4299861712
>>> id(b.instances)
4299861712
>>> a = A(3) #original a will be dereferenced and replaced with new instance
>>> A.get_instances()
[<inst.A object at 0x100587290>, <inst.A object at 0x1005872d0>]   


Answer 4:

你也可以使用一个解决这个问题的元类 :

  1. 当创建一个类( __init__元类的方法),添加一个新的实例注册表
  2. 当创建这个类的一个新实例( __call__元类的方法),将其添加到实例注册表。

这种方法的好处是,每个类都有一个注册表 - 即使没有实例存在。 相反,当覆盖__new__ (如Blckknght的答案 ),注册表是在创建第一个实例时添加。

class MetaInstanceRegistry(type):
    """Metaclass providing an instance registry"""

    def __init__(cls, name, bases, attrs):
        # Create class
        super(MetaInstanceRegistry, cls).__init__(name, bases, attrs)

        # Initialize fresh instance storage
        cls._instances = weakref.WeakSet()

    def __call__(cls, *args, **kwargs):
        # Create instance (calls __init__ and __new__ methods)
        inst = super(MetaInstanceRegistry, cls).__call__(*args, **kwargs)

        # Store weak reference to instance. WeakSet will automatically remove
        # references to objects that have been garbage collected
        cls._instances.add(inst)

        return inst

    def _get_instances(cls, recursive=False):
        """Get all instances of this class in the registry. If recursive=True
        search subclasses recursively"""
        instances = list(cls._instances)
        if recursive:
            for Child in cls.__subclasses__():
                instances += Child._get_instances(recursive=recursive)

        # Remove duplicates from multiple inheritance.
        return list(set(instances))

用法:创建一个注册表和它的子类。

class Registry(object):
    __metaclass__ = MetaInstanceRegistry


class Base(Registry):
    def __init__(self, x):
        self.x = x


class A(Base):
    pass


class B(Base):
    pass


class C(B):
    pass


a = A(x=1)
a2 = A(2)
b = B(x=3)
c = C(4)

for cls in [Base, A, B, C]:
    print cls.__name__
    print cls._get_instances()
    print cls._get_instances(recursive=True)
    print

del c
print C._get_instances()

如果使用从抽象基类abc模块,只要继承abc.ABCMeta避免冲突元类:

from abc import ABCMeta, abstractmethod


class ABCMetaInstanceRegistry(MetaInstanceRegistry, ABCMeta):
    pass


class ABCRegistry(object):
    __metaclass__ = ABCMetaInstanceRegistry


class ABCBase(ABCRegistry):
    __metaclass__ = ABCMeta

    @abstractmethod
    def f(self):
        pass


class E(ABCBase):
    def __init__(self, x):
        self.x = x

    def f(self):
        return self.x

e = E(x=5)
print E._get_instances()


Answer 5:

使用从@Joel科内特我已经想出以下,这似乎工作答案。 即我能总量可达对象变量。

import os

os.system("clear")

class Foo():
    instances = []
    def __init__(self):
        Foo.instances.append(self)
        self.x = 5

class Bar():
    def __init__(self):
        pass

    def testy(self):
        self.foo1 = Foo()
        self.foo2 = Foo()
        self.foo3 = Foo()

foo = Foo()
print Foo.instances
bar = Bar()
bar.testy()
print Foo.instances

x_tot = 0
for inst in Foo.instances:
    x_tot += inst.x
    print x_tot

输出:

[<__main__.Foo instance at 0x108e334d0>]
[<__main__.Foo instance at 0x108e334d0>, <__main__.Foo instance at 0x108e33560>, <__main__.Foo instance at 0x108e335a8>, <__main__.Foo instance at 0x108e335f0>]
5
10
15
20


Answer 6:

快速低级别的黑客和调试的另一种选择是,以滤除返回的对象列表gc.get_objects()和动态生成字典的方式。 在CPython的该函数将返回你的一切垃圾收集知道的(通常巨大的)名单,所以它肯定会包含所有任何特定用户定义的类的实例。

请注意,这是挖掘有点到解释器的内部,因此它可能会或可能不会与我没有检查的Jython,PyPy,IronPython的,等的喜欢的工作(或正常工作)。 它也可能是很慢不管。 慎用/因人而异的/ etc使用。

不过,我想,有些人遇到了此问题可能最终想要做这种作为一次性的事情弄清楚发生了什么事情与一些代码片段是真实的行为出现异常的运行状态。 此方法具有不影响实例或他们的建筑在所有的好处,如果有问题的代码是走出第三方库之类的东西,这可能是有用的。



文章来源: How to keep track of class instances?