2014-02-09 12 views
10

Ich habe eine list von benutzerdefinierten Objekten (Beispiel ist unten).Wie kann ein benutzerdefiniertes Objekt iterierbar gemacht werden?

Verwenden: list(itertools.chain.from_iterable(myBigList)) Ich wollte alle Unterlisten stations in eine große Liste "fusionieren". Also dachte ich, ich muss meine benutzerdefinierte Klasse iterierbar machen.

Hier ist ein Beispiel meiner benutzerdefinierten Klasse.

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 umgesetzt __iter__ und __next__ aber es scheint nicht zu funktionieren. Sie werden nicht einmal angerufen.

Irgendeine Idee was könnte ich falsch gemacht haben?

Hinweis: Die Verwendung Python 3.3

+0

Sind Subklassen Sie 'list'? – dawg

+0

Nein, eine Liste ist nur ein Mitglied meiner Klasse. –

+0

Ist das die tatsächliche Einrückung? '__iter__' und' __next__' sind außerhalb Ihrer Klassendefinition. – jonrsharpe

Antwort

12

__iter__ ist, was aufgerufen wird, wenn Sie versuchen, eine Instanz der Klasse iterieren:

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

__next__ ist das, was auf dem Objekt aufgerufen wird, die von __iter__ zurückgegeben wird (auf Python2.x, es ist next, nicht __next__ - ich alias sie beide beide, so dass der Code mit entweder funktioniert ...):

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. 
+0

Nun, ich bin wirklich verwirrt, denn ich war mein Code um das war falsch. Aber Ihre Antwort hat es mir ein bisschen klarer gemacht, wie iterierbar ist! –

+0

Warum nicht einfach den Iterator von 'self.data' zurückgeben? Für OP-Zwecke ist das zwar gut, aber nicht threadsicher. Sie können 'next()' auf dem Iterator aufrufen, bevor Sie ihn zurückgeben, wenn Sie das erste Element überspringen wollen. –

+0

@MadPhysicist - Das ist ein guter Punkt. Es ist 2 Jahre her, seit ich das beantwortet habe, aber ich denke, dass der Grund, warum ich es so gemacht habe, OP zu demonstrieren war, wie man das Iterator-Protokoll mit '__iter__' und' __next__' verwendet. – mgilson

3

einfach implementieren __iter__ sollte ausreichen.

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)])) 

Ausgang:

[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] 

Siehe here warum es _direction__stations

Jede Kennung der Form __spam (mindestens zwei führenden Unterstrichen, höchstens einen hinteren Unterstrich) ist textlich ersetzt durch Klassenname _spam, wobei Klassenname die aktuelle Klasse ist Name mit führenden Unterstrichen

+0

Sie müssen 'iter (self .__ stations [1:])' 'verwenden, da der OP-Code das erste Element überspringt. –

+0

Ich werde es als eine Option mit Code auskommentieren. weil er nicht explizit gesagt hat, dass er das wollte. Ich werde davon ausgehen, dass die natürliche Umsetzung aller Elemente geben. Und dass seine ursprüngliche Implementierung von "next" einen kleinen Fehler enthielt. – M4rtini

+2

Oder, x = iter (self .__ Stationen); nächstes (x); return x' wenn du keine unnötige Kopie machen willst ;-) – mgilson

1

können Sie list Unterklasse auch:

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 

dh die erste überspringt.

Works für verschachtelte Listen auch:

>>> 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]   
Verwandte Themen