如何跟踪类实例?

dws*_*ein 19 python dictionary class self instances

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

例如:

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

foo1 = Foo()
foo2 = Foo()
foo...etc.
Run Code Online (Sandbox Code Playgroud)

假设实例的数量会有所不同,我希望每个Foo()实例的x dict都加载到一个新的dict中.我该怎么办?

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

Joe*_*ett 33

跟踪实例的一种方法是使用类变量:

class A(object):
    instances = []

    def __init__(self, foo):
        self.foo = foo
        A.instances.append(self)
Run Code Online (Sandbox Code Playgroud)

在程序结束时,您可以像这样创建你的dict:

foo_vars = {id(instance): instance.foo for instance in A.instances}
Run Code Online (Sandbox Code Playgroud)

只有一个清单:

>>> 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    
Run Code Online (Sandbox Code Playgroud)

  • @dwstein:不,请看编辑.`instances`是一个类变量.这是一个相关的概念:[Python中的"最小惊讶":可变默认参数](http://stackoverflow.com/q/1132941/1142167) (2认同)

Blc*_*ght 25

@ 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
Run Code Online (Sandbox Code Playgroud)

这解决了@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)
[]
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,WeakSet 将使用标准散列语义而不是身份语义,这意味着如果 OP 的基类想要覆盖 `__eq__`,它会在没有相应的 `__hash__` 覆盖的情况下出错,并且即使使用覆盖,它仍然会出现错误行为因为它将合并相等的对象。 (2认同)

小智 8

您可能希望对实例使用弱引用.否则,该类可能最终会跟踪要删除的实例.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
Run Code Online (Sandbox Code Playgroud)

在程序结束时,您可以像这样创建你的dict:

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>]   
Run Code Online (Sandbox Code Playgroud)

  • 这很有趣,但并不完全可靠:当一个引用被删除(`del a`)时,它可能不会在下一行的实例集之外,特别是如果同时处理了一个异常。有关更多详细信息,请参阅我在 [此处] 提出的问题(/sf/ask/3397008611/) . (2认同)

归档时间:

查看次数:

20825 次

最近记录:

7 年,10 月 前