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.