我可以在Python中迭代一个类吗?

xor*_*yst 25 python

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

class Foo:
    by_id = {}

    def __init__(self, id):
        self.id = id
        self.by_id[id] = self
Run Code Online (Sandbox Code Playgroud)

我希望能够做的是迭代现有的类实例.我可以这样做:

for foo in Foo.by_id.values():
    foo.do_something()
Run Code Online (Sandbox Code Playgroud)

但它看起来像这样整洁:

for foo in Foo:
    foo.do_something()
Run Code Online (Sandbox Code Playgroud)

这可能吗?我尝试定义一个类方法__iter__,但这没有用.

glg*_*lgl 28

如果要迭代,则必须定义支持迭代的元类.

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

正如您在最后看到的那样,删除实例不会对对象本身产生任何影响,因为它保留在by_iddict中.weakref当你使用s时,你可以应付

import weakref
Run Code Online (Sandbox Code Playgroud)

然后呢

by_id = weakref.WeakValueDictionary()
Run Code Online (Sandbox Code Playgroud)

.这样,只要有一个"强"引用保留它,这些值就会保留,例如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)
Run Code Online (Sandbox Code Playgroud)

看起来有点复杂,但确保您获得对象而不是weakref对象.

  • 很好的触摸,将迭代委托给类上的方法. (4认同)
  • PEP8建议不要发明带有“ __classiter__”之类的双下划线的名称。 (2认同)

Joh*_*ooy 9

魔术方法总是在类上查找,因此添加__iter__到类中将不会使其可迭代.但是,类是其元类的实例,因此元类是定义__iter__方法的正确位置.

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

class Foo:
    __metaclass__ = FooMeta
    ...
Run Code Online (Sandbox Code Playgroud)