Can I iterate over a class in Python?

python

Solution

If you want to iterate over the class, you have to define a metaclass which supports iteration.

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)

As you can see in the end, deleting an instance will not have any effect on the object itself, because it stays in the `by_id` dict. You can cope with that using `weakref`s when you

import weakref

and then do

by_id = weakref.WeakValueDictionary()

. This way the values will only kept as long as there is a "strong" reference keeping it, such as `a` in this case. After `del a`, there are only weak references pointing to the object, so they can be gc'ed.

Due to the warning concerning `WeakValueDictionary()`s, I suggest to use the following:

[...]
    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)

Looks a bit complicated, but makes sure that you get the objects and not a `weakref` object.

Problem

I have a class that keeps track of its instances in a class variable, something like this: ``` class Foo: by_id = {} def __init__(self, id): self.id = id self.by_id[id] = self ``` What I'd like to be able to do is iterate over the existing instances of the class. I can do this with: ``` for foo in Foo.by_id.values(): foo.do_something() ``` but it would look neater like this: ``` for foo in Foo: foo.do_something() ``` is this possible? I tried defining a classmethod `__iter__`, but that didn't work.

Original source