Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a custom object iterable?

I have a list of custom-class objects (sample is below).

Using: list(itertools.chain.from_iterable(myBigList)) I wanted to "merge" all of the stations sublists into one big list. So I thought I need to make my custom class an iterable.

Here is a sample of my custom class.

class direction(object) :     def __init__(self, id) :         self.id = id                       self.__stations = list()      def __iter__(self):         self.__i = 0                #  iterable current item          return iter(self.__stations)      def __next__(self):         if self.__i<len(self.__stations)-1:             self.__i += 1                      return self.__stations[self.__i]         else:             raise StopIteration 

I implemented __iter__ and __next__ but it doesn't seems to work. They're not even called.

Any idea what could I've done wrong?

Note: Using Python 3.3

like image 564
Matthieu Riegler Avatar asked Feb 09 '14 21:02

Matthieu Riegler


People also ask

How do you make an object iterable?

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 .

How do you make something iterable in Python?

To make your class Iterable we need to override __iter__() function inside our class i.e. This function should return the object of Iterator class associated with this Iterable class. Contains List of Junior and senior team members and also overrides the __iter__() function. It overrides the __iter__() function.

Can objects be iterable?

The iterable protocol allows JavaScript objects to define or customize their iteration behavior, such as what values are looped over in a for...of construct. Some built-in types are built-in iterables with a default iteration behavior, such as Array or Map , while other types (such as Object ) are not.

How do I fix TypeError type object is not iterable in Python?

The Python "TypeError: 'type' object is not iterable" occurs when we try to iterate over a class that is not iterable, e.g. forget to call the range() function. To solve the error, make the class iterable by implementing the __iter__() method.


2 Answers

__iter__ is what gets called when you try to iterate over a class instance:

>>> class Foo(object): ...     def __iter__(self): ...         return (x for x in range(4)) ... >>> list(Foo()) [0, 1, 2, 3] 

__next__ is what gets called on the object which is returned from __iter__ (on python2.x, it's next, not __next__ -- I generally alias them both so that the code will work with either...):

class Bar(object):     def __init__(self):         self.idx = 0         self.data = range(4)     def __iter__(self):         return self     def __next__(self):         self.idx += 1         try:             return self.data[self.idx-1]         except IndexError:             self.idx = 0             raise StopIteration  # Done iterating.     next = __next__  # python2.x compatibility. 

In the comments, it was asked how you would construct and object that could be iterated multiple times. In this case, I'd recommend taking the same approach that Python takes and split the iterator from the data container:

class BarIterator(object):     def __init__(self, data_sequence):         self.idx = 0         self.data = data_sequence     def __iter__(self):         return self     def __next__(self):         self.idx += 1         try:             return self.data[self.idx-1]         except IndexError:             self.idx = 0             raise StopIteration  # Done iterating.   class Bar(object):     def __init__(self, data_sequence):         self.data_sequence = data_sequence     def __iter__(self):         return BarIterator(self.data_sequence) 
like image 154
mgilson Avatar answered Sep 29 '22 04:09

mgilson


simply implementing __iter__ should be enough.

class direction(object) :     def __init__(self, id) :         self.id = id                       self.__stations = list()      def __iter__(self):         #return iter(self.__stations[1:]) #uncomment this if you wanted to skip the first element.         return iter(self.__stations)   a = direction(1) a._direction__stations= range(5)  b = direction(1) b._direction__stations = range(10)  import itertools print list(itertools.chain.from_iterable([a,b])) print list(itertools.chain.from_iterable([range(5),range(10)])) 

output:

[0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

See here for why it's _direction__stations

Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with classname_spam, where classname is the current class name with leading underscore(s) stripped.

like image 30
M4rtini Avatar answered Sep 29 '22 06:09

M4rtini