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.
These include the string, list, tuple, dict, set, and frozenset types. But these are by no means the only types that you can iterate over. Many objects that are built into Python or defined in modules are designed to be iterable.
In Python, Set is an unordered collection of data type that is iterable, mutable and has no duplicate elements. There are numerous ways that can be used to iterate over a Set.
The __iter__() function returns an iterator for the given object (array, set, tuple, etc. or custom objects). It creates an object that can be accessed one element at a time using __next__() function, which generally comes in handy when dealing with loops.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With