Can I iterate over a class in Python?
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.
Magic methods are always looked up on the class, so adding __iter__
to the class won't make it iterable. However the class is an instance of its metaclass, so the metaclass is the correct place to define the __iter__
method.
class FooMeta(type):
def __iter__(self):
return self.by_id.iteritems()
class Foo:
__metaclass__ = FooMeta
...