如何跟踪类实例?

2025-01-17 09:23:00
admin
原创
77
摘要:问题描述:在程序即将结束时,我希望将某个类的所有实例中的特定变量加载到字典中。例如:class Foo(): def __init__(self): self.x = {} foo1 = Foo() foo2 = Foo() ... 假设实例的数量会有所不同,并且我希望将 Foo()...

问题描述:

在程序即将结束时,我希望将某个类的所有实例中的特定变量加载到字典中。

例如:

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

foo1 = Foo()
foo2 = Foo()
...

假设实例的数量会有所不同,并且我希望将 Foo() 的每个实例的 x 字典加载到新的字典中。我该怎么做?

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


解决方案 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    

解决方案 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在代码中的其他地方重新分配对实例的所有其他引用,则簿记代码不会阻止它被垃圾收集。同样,对于某些用例来说,这可能不是理想的选择,但如果您真的希望每个实例永远存在,那么使用常规集合(或列表)而不是弱集就足够了。

一些方便的测试输出(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)
[]

解决方案 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): instance.foo for instance in 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>]   

解决方案 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()

解决方案 5:

另一种快速低级黑客攻击和调试的方法是过滤返回的对象列表gc.get_objects()并以此方式动态生成字典。在 CPython 中,该函数将返回垃圾收集器所知道的所有内容的列表(通常很大) ,因此它肯定会包含任何特定用户定义类的所有实例。

请注意,这会深入解释器的内部,因此它可能与 Jython、PyPy、IronPython 等兼容(或兼容良好),也可能不兼容。我还没有检查过。无论如何,它也可能非常慢。请谨慎使用/YMMV/等等。

不过,我想遇到这个问题的一些人最终可能会想一次性做这样的事情来弄清楚某些行为异常的代码片段的运行时状态发生了什么。这种方法的好处是完全不影响实例或其构造,如果相关代码来自第三方库或类似的东西,这可能会很有用。

解决方案 6:

使用@Joel Cornett 的答案,我得出了下面的结论,似乎有效。也就是说,我能够计算出对象变量的总和。

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

解决方案 7:

这里有一个与 Blckknght 类似的方法,也适用于子类。如果有人最终来到这里,我认为这可能会很有趣。一个区别是,如果 B 是 A 的子类,而 b 是 B 的实例,则 b 将同时出现在 A.instances 和 B.instances 中。正如 Blckknght 所说,这取决于用例。

from weakref import WeakSet


class RegisterInstancesMixin:
    instances = WeakSet()

    def __new__(cls, *args, **kargs):
        o = object.__new__(cls, *args, **kargs)
        cls._register_instance(o)
        return o

    @classmethod
    def print_instances(cls):
        for instance in cls.instances:
            print(instance)

    @classmethod
    def _register_instance(cls, instance):
        cls.instances.add(instance)
        for b in cls.__bases__:
            if issubclass(b, RegisterInstancesMixin):
                b._register_instance(instance)

    def __init_subclass__(cls):
        cls.instances = WeakSet()


class Animal(RegisterInstancesMixin):
    pass


class Mammal(Animal):
    pass


class Human(Mammal):
    pass


class Dog(Mammal):
    pass


alice = Human()
bob = Human()
cannelle = Dog()
Animal.print_instances()
Mammal.print_instances()
Human.print_instances()

Animal.print_instances()将打印三个对象,而Human.print_instances()将打印两个。

解决方案 8:

(对于 Python)我找到了一种在定义类时通过“dataclass”装饰器记录类实例的方法。将类属性“instances”(或任何其他名称)定义为要记录的实例列表。通过 dunder 方法将该列表附加到创建对象的“dict”形式中__dict__。因此,类属性“instances”将以您想要的 dict 形式记录实例。

例如,

from dataclasses import dataclass

@dataclass
class player:
    instances=[]
    def __init__(self,name,rank):
        self.name=name
        self.rank=rank
        self.instances.append(self.__dict__)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用