Python: Make class iterable
Solution 1
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
Solution 2
this is how we make a class object iterable. provide the class with a iter and a next() method, then you can iterate over class attributes or their values.you can leave the next() method if you want to, or you can define next() and raise StopIteration on some condition.
e.g:
class Book(object):
def __init__(self,title,author):
self.title = title
self.author = author
def __iter__(self):
for each in self.__dict__.values():
yield each
>>> book = Book('The Mill on the Floss','George Eliot')
>>> for each in book: each
...
'George Eliot'
'The Mill on the Floss'
this class iterates over attribute value of class Book. A class object can be made iterable by providing it with a getitem method too. e.g:
class BenTen(object):
def __init__(self, bentenlist):
self.bentenlist = bentenlist
def __getitem__(self,index):
if index <5:
return self.bentenlist[index]
else:
raise IndexError('this is high enough')
>>> bt_obj = BenTen([x for x in range(15)])
>>>for each in bt_obj:each
...
0
1
2
3
4
now when the object of BenTen class is used in a for-in loop, getitem is called with succesively higher index value, till it raises IndexError.
Solution 3
You can iterate over the class's unhidden attributes with for attr in (elem for elem in dir(Foo) if elem[:2] != '__')
.
A less horrible way to spell that is:
def class_iter(Class):
return (elem for elem in dir(Class) if elem[:2] != '__')
then
for attr in class_iter(Foo):
pass
Related videos on Youtube
multipleinterfaces
This space is reserved for future use. It must be NULL.
Updated on April 15, 2022Comments
-
multipleinterfaces almost 2 years
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 evenif 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
-
user2357112 almost 4 yearsIf you want to check whether an attribute is present, the way to do that is
hasattr
. You don't need to make your class iterable.
-
-
NPE about 13 yearsNice. Please could you explain why the OP's approach doesn't work? Thanks.
-
Xavier Combelle about 13 yearsI must admit I prefer this solution which looks more pythonic than the one of OP. But has it's not solve his problem I did not +1
-
nmichaels about 13 years@aix: The reason OP's approach doesn't work is that the
__iter__
method only works for instances of the class. This bumps the__iter__
method up to instances of the metaclass, i.e. the class. -
Sven Marnach about 13 years@aix: As other magic methods,
__iter__
is looked up in the name space of the type of the object rather than in the object's name space itself. I did not really find this explained in the Python docs, but it can be easily seen in the source code (search for the definition ofPyObject_GetIter()
). -
multipleinterfaces about 11 yearsThis iterates over the attributes of an instance of a class (i.e. the
book
inbook = Book(...)
); the question is about iterating over the class attributes directly (i.e. theBook
inclass Book(object):
). -
dlite922 about 11 yearsAlthough this is not the answer to OP's question, it helped me because I was looking for this when searching for iterable class.
-
trudolf almost 9 yearsCould someone explain why it doesn't work, when writing the __iter__() method as a
@classmethod
on theFoo
class? @nmichaels answer doesn't explain this I think. ` @classmethod def __iter__(cls): for x in xrange(10): yield x ` -
Sven Marnach almost 9 years@trudolf: Special methods are looked up on the type of the instance, not on the instance itself. If the instance is a class, this means the special method is looked up on the metaclass, not the class itself.
-
user2357112 almost 4 yearsThis completely changes the behavior of the class and all its attributes. Making the class iterable is a minor side effect. It's not a solution to the general problem of making classes iterable.
-
Asclepius almost 4 years@user2357112supportsMonica First, that's a weird username. Second, I have now updated the answer to mention that this approach is not a general solution. It is nevertheless useful for some use cases.
-
K. Symbol about 2 yearsIt should be __iter__() and __next__()?