Как сделать пользовательский объект итерабельным?
У меня есть list
объектов пользовательского класса (пример ниже).
Использование: list(itertools.chain.from_iterable(myBigList))
Я хотел "объединить" все подстраницы stations
в один большой список. Поэтому я подумал, что мне нужно сделать свой пользовательский класс итерабельным.
Вот пример моего пользовательского класса.
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
Я реализовал __iter__
и __next__
, но он не работает. Их даже не называют.
Любая идея, что я мог сделать неправильно?
Примечание. Использование Python 3.3
Ответы
Ответ 1
__iter__
- это то, что вызывается при попытке выполнить итерацию над экземпляром класса:
>>> class Foo(object):
... def __iter__(self):
... return (x for x in range(4))
...
>>> list(Foo())
[0, 1, 2, 3]
__next__
- это то, что вызывается на объекте, который возвращается из __iter__
(на python2.x, it next
, not __next__
). Я обычно называю их обоими так, чтобы код работал с...):
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.
Ответ 2
просто реализовать __iter__
должно быть достаточно.
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)]))
вывод:
[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]
Смотрите здесь, почему он _direction__stations
Любой идентификатор формы __spam (по крайней мере, два ведущих символа подчеркивания, не более одного нижнего подчеркивания) заменяется на текст classname_spam, где classname - это текущее имя класса с лидирующим подчеркиванием (s).
Ответ 3
Вы также можете подклассифицировать list
:
class Direction(list):
def __init__(self, seq=[], id_=None):
list.__init__(self,seq)
self.id = id_ if id_ else id(self)
def __iter__(self):
it=list.__iter__(self)
next(it) # skip the first...
return it
d=Direction(range(10))
print(d) # all the data, no iteration
# [0, 1, 2, 3, 4]
print (', '.join(str(e) for e in d)) # 'for e in d' is an iterator
# 1, 2, 3, 4
т.е. пропускает первый.
Работает и с вложенными списками:
>>> d1=Direction([range(5), range(10,15), range(20,25)])
>>> d1
[range(0, 5), range(10, 15), range(20, 25)]
print(list(itertools.chain.from_iterable(d1)))
[10, 11, 12, 13, 14, 20, 21, 22, 23, 24]