简体   繁体   English

如何在python中获取某个类的所有实例?

[英]How to get all instances of a certain class in python?

Someone asked a similar one [question]: Printing all instances of a class .有人问了一个类似的[问题]: 打印一个类的所有实例 While I am less concerned about printing them, I'd rather to know how many instances are currently "live".虽然我不太关心打印它们,但我更想知道当前“活动”的实例有多少。 The reason for this instance capture is more like a setting up a scheduled job, every hour check these "live" unprocessed instances and enrich the data.这种实例捕获的原因更像是设置计划作业,每小时检查这些“实时”未处理的实例并丰富数据。 After that, either a flag in this instance is set or just delete this instance.之后,要么设置此实例中的标志,要么删除此实例。 Torsten Marek 's answer in [question]: Printing all instances of a class using weakrefs need a call to the base class constructor for every class of this type, is it possible to automate this? Torsten Marek 在 [问题] 中的回答:使用弱引用打印类的所有实例需要为这种类型的每个类调用基类构造函数,是否可以自动执行此操作? Or we can get all instances with some other methods?或者我们可以使用其他方法获取所有实例?

You can either track it on your own (see the other answers) or ask the garbage collector:您可以自己跟踪它(请参阅其他答案)或询问垃圾收集器:

import gc

class Foo(object):
   pass

foo1, foo2 = Foo(), Foo()

foocount = sum(1 for o in gc.get_referrers(Foo) if o.__class__ is Foo)

This can be kinda slow if you have a lot of objects, but it's generally not too bad, and it has the advantage of being something you can easily use with someone else's code.如果您有很多对象,这可能会有点慢,但通常不会太糟糕,而且它的优点是您可以轻松地将其与其他人的代码一起使用。

Note: Used o.__class__ rather than type(o) so it works with old-style classes.注意:使用o.__class__而不是type(o)所以它适用于旧式类。

If you only want this to work for CPython, and your definition of "live" can be a little lax, there's another way to do this that may be useful for debugging/introspection purposes:如果您只希望它适用于 CPython,并且您对“实时”的定义可能有点松懈,那么还有另一种方法可以用于调试/内省目的:

>>> import gc
>>> class Foo(object): pass
>>> spam, eggs = Foo(), Foo()
>>> foos = [obj for obj in gc.get_objects() if isinstance(obj, Foo)]
>>> foos
[<__main__.Foo at 0x1153f0190>, <__main__.Foo at 0x1153f0210>]
>>> del spam
>>> foos = [obj for obj in gc.get_objects() if isinstance(obj, Foo)]
>>> foos
[<__main__.Foo at 0x1153f0190>, <__main__.Foo at 0x1153f0210>]
>>> del foos
>>> foos = [obj for obj in gc.get_objects() if isinstance(obj, Foo)]
>>> foos
[<__main__.Foo at 0x1153f0190>]

Note that deleting spam didn't actually make it non-live, because we've still got a reference to the same object in foos .请注意,删除spam实际上并没有使其foos ,因为我们仍然在foos获得了对同一对象的foos And reassigning foos didn't not help, because apparently the call to get_objects happened before the old version is released.重新分配foos并没有帮助,因为显然对get_objects的调用发生在旧版本发布之前。 But eventually it went away once we stopped referring to it.但最终一旦我们不再提及它,它就消失了。

And the only way around this problem is to use weakrefs.解决这个问题的唯一方法是使用弱引用。

Of course this will be horribly slow in a large system, with or without weakrefs.当然,这在大型系统中会非常慢,无论有没有弱引用。

Sure, store the count in a class attribute:当然,将计数存储在类属性中:

class CountedMixin(object):
    count = 0
    def __init__(self, *args, **kwargs):
        type(self).count += 1
        super().__init__(*args, **kwargs)
    def __del__(self):
        type(self).count -= 1
        try:
            super().__del__()
        except AttributeError:
            pass

You could make this slightly more magical with a decorator or a metaclass than with a base class, or simpler if it can be a bit less general (I've attempted to make this fit in anywhere in any reasonable multiple-inheritance hierarchy, which you usually don't need to worry about…), but basically, this is all there is to it.您可以使用装饰器或元类使其比使用基类更神奇,或者如果它不那么通用则更简单(我试图使其适合任何合理的多继承层次结构中的任何地方,您通常不需要担心......),但基本上,这就是它的全部。

If you want to have the instances themselves (or, better, weakrefs to them), rather than just a count of them, just replace count=0 with instances=set() , then do instances.add(self) instead of count += 1 , etc. (Again, though, you probably want a weakref to self , rather than self .)如果您想拥有实例本身(或者,更好的是,对它们进行弱引用),而不仅仅是对它们的计数,只需将count=0替换为instances=set() ,然后执行instances.add(self)而不是count += 1等(同样,你可能想要一个弱引用self ,而不是self 。)

I cannot comment to the answer of kindall, thus I write my comment as answer:我无法评论kindall的答案,因此我将评论写为答案:

The solution with gc.get_referrers(<ClassName>) does not work with inherited classes in python 3. The method gc.get_referrers(<ClassName>) does not return any instances of a class that was inherited from <ClassName> . gc.get_referrers(<ClassName>)的解决方案不适用于 python 3 中的继承类。 gc.get_referrers(<ClassName>)不返回从<ClassName>继承的类的任何实例。

Instead you need to use gc.get_objects() which is much slower, since it returns a full list of objects.相反,您需要使用慢得多的gc.get_objects() ,因为它返回一个完整的对象列表。 But in case of unit-tests, where you simply want to ensure your objects get deleted after the test (no circular references) it should be sufficient and fast enough.但是在单元测试的情况下,您只想确保在测试后删除您的对象(没有循环引用),它应该足够且足够快。

Also do not forget to call gc.collect() before checking the number of your instances, to ensure all unreferenced instances are really deleted.另外不要忘记在检查实例数量之前调用gc.collect() ,以确保所有未引用的实例都被真正删除。

I also saw an issue with weak references which are also counted in this way.我还看到了一个弱引用的问题,这也是以这种方式计算的。 The problem with weak references is, that the object which is referenced might not exist any more, thus isinstance(Instance, Class) might fail with an error about non existing weak references.弱引用的问题是,被引用的对象可能不再存在,因此isinstance(Instance, Class)可能会失败并出现关于不存在的弱引用的错误。

Here is a simple code example:下面是一个简单的代码示例:

import gc

def getInstances(Class):
  gc.collect()
  Number = 0
  InstanceList = gc.get_objects()
  for Instance in InstanceList:
    if 'weakproxy' not in str(type(Instance)): # avoid weak references
      if isinstance(Instance, Class):
        Number += 1

  return Number

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

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