30

我有一个类在类变量中跟踪其实例,如下所示:

class Foo:
    by_id = {}

    def __init__(self, id):
        self.id = id
        self.by_id[id] = self

我想做的是遍历类的现有实例。我可以这样做:

for foo in Foo.by_id.values():
    foo.do_something()

但它看起来像这样更整洁:

for foo in Foo:
    foo.do_something()

这可能吗?我尝试定义一个 classmethod __iter__,但这没有用。

4

4 回答 4

32

如果你想迭代,你必须定义一个支持迭代的元类。

x.py:

class it(type):
    def __iter__(self):
        # Wanna iterate over a class? Then ask that class for iterator.
        return self.classiter()

class Foo:
    __metaclass__ = it # We need that meta class...
    by_id = {} # Store the stuff here...

    def __init__(self, id): # new isntance of class
        self.id = id # do we need that?
        self.by_id[id] = self # register istance

    @classmethod
    def classiter(cls): # iterate over class by giving all instances which have been instantiated
        return iter(cls.by_id.values())

if __name__ == '__main__':
    a = Foo(123)
    print list(Foo)
    del a
    print list(Foo)

正如您最后看到的,删除实例不会对对象本身产生任何影响,因为它保留在by_id字典中。weakref当你

import weakref

然后做

by_id = weakref.WeakValueDictionary()

. 这样,只要有一个“强”引用保存它,这些值就会保留,例如a在这种情况下。之后del a,只有指向该对象的弱引用,所以它们可以被 gc'ed。

由于有关WeakValueDictionary()s 的警告,我建议使用以下内容:

[...]
    self.by_id[id] = weakref.ref(self)
[...]
@classmethod
def classiter(cls):
    # return all class instances which are still alive according to their weakref pointing to them
    return (i for i in (i() for i in cls.by_id.values()) if i is not None)

看起来有点复杂,但要确保你得到的是对象而不是weakref对象。

于 2012-05-30T10:41:56.900 回答
10

魔术方法总是在类中查找,因此添加__iter__到类不会使其可迭代。然而,该类是其元类的一个实例,因此元类是定义该__iter__方法的正确位置。

class FooMeta(type):
    def __iter__(self):
        return self.by_id.iteritems()

class Foo:
    __metaclass__ = FooMeta
    ...
于 2012-05-30T10:45:32.577 回答
4

试试这个:

您可以创建一个具有全局范围的列表,在主模块中定义一个列表,如下所示:

fooList = []

然后加:

class Foo:
  def __init__(self):
    fooList.append(self)

到foo 类的init

然后每次创建 Foo 类的实例时,它都会被添加到 fooList 列表中。

现在您所要做的就是像这样遍历对象数组

for f in fooList:
    f.doSomething()
于 2015-08-21T21:46:57.283 回答
1

您可以创建一个类列表,然后在 init 方法中调用 append,如下所示:

class Planet:
  planets_list = []

  def __init__(self, name):
     self.name = name
     self.planets_list.append(self)

用法:

p1 = Planet("earth")
p2 = Planet("uranus")

for i in Planet.planets_list:
    print(i.name)
于 2015-08-23T03:37:34.837 回答