I have inherited a project with many large classes constituent of nothing but class objects (integers, strings, etc). I'd like to be able to check if an attribute is present without needed to define a list of attributes manually.
Is it possible to make a python class iterable itself using the standard syntax? That is, I'd like to be able to iterate over all of a class's attributes using for attr in Foo:
(or even if attr in Foo
) without needing to create an instance of the class first. I think I can do this by defining __iter__
, but so far I haven't quite managed what I'm looking for.
I've achieved some of what I want by adding an __iter__
method like so:
class Foo: bar = "bar" baz = 1 @staticmethod def __iter__(): return iter([attr for attr in dir(Foo) if attr[:2] != "__"])
However, this does not quite accomplish what I'm looking for:
>>> for x in Foo: ... print(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'classobj' object is not iterable
Even so, this works:
>>> for x in Foo.__iter__(): ... print(x) bar baz
To make a class as iterable, you have to implement the __iter__() and __next__() methods in that class. The __iter__() method allows us to make operations on the items or initializing the items and returns an iterator object.
If you are running your Python code and you see the error “TypeError: 'int' object is not iterable”, it means you are trying to loop through an integer or other data type that loops cannot work on. In Python, iterable data are lists, tuples, sets, dictionaries, and so on.
To make the range object iterable (and thus let for..of work) we need to add a method to the object named Symbol. iterator (a special built-in symbol just for that). When for..of starts, it calls that method once (or errors if not found). The method must return an iterator – an object with the method next .
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.
Add the __iter__
to the metaclass instead of the class itself (assuming Python 2.x):
class Foo(object): bar = "bar" baz = 1 class __metaclass__(type): def __iter__(self): for attr in dir(self): if not attr.startswith("__"): yield attr
For Python 3.x, use
class MetaFoo(type): def __iter__(self): for attr in dir(self): if not attr.startswith("__"): yield attr class Foo(metaclass=MetaFoo): bar = "bar" baz = 1
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