2016-04-11 9 views
0

Von zwei Optionen unten, was ansprechender (lesbarer Code, mehr Pythonie, Effizienz, etc ..) für das Durchlaufen iterable und wenn ich mehr Logik in der Zukunft hinzufügen möchte (zum Beispiel hinzufügen 1 zu jedem zurückgegebenen Wert)?Return-Generator VS iterieren mit while bis None

Eine der Optionen gibt den Generator zurück, die andere gibt das nächste Element und None zurück, wenn alle Iterationen durchgeführt wurden.

Gibt es eine bevorzugte Methode? Wenn ja, warum? Gibt es Einschränkungen, die eine Methode hat, die andere nicht?

class Option1(): 
    def __init__(self): 
     self.current = 0 

    def get_next_batch(self, my_list): 
     if self.current == len(my_list): 
      self.current = 0 
      return None 
     self.current += 1 
     return [my_list[self.current-1]] 


class Option2(): 
     def __init__(self): 
      self.current = 0 

     def get_next_batch(self, my_list): 
      while self.current < len(my_list): 
       yield [my_list[self.current]] 
       self.current += 1 
      self.current = 0 
      raise StopIteration() 

Verbrauch:

o1 = Option1() 
o2 = Option2() 
arr = [1,2,3,4,5] 

a = o1.get_next_batch(arr) 

while a is not None: 
    print a 
    a = o1.get_next_batch(arr) 

for item in o2.get_next_batch(arr): 
    print item 

Ausgang in beiden Fällen:

[1] 
[2] 
[3] 
[4] 
[5] 

Antwort

1

Sie wollen an Sicherheit grenzender Wahrscheinlichkeit mit dem zweiten gehen. Weniger Linien, weniger Möglichkeiten, dass etwas schief geht.

jedoch sehen, wie Sie nicht current außerhalb des Iterator würde ich nur die ganze Sache optimieren bis zu:

def get_next_batch(my_list): 
    for item in my_list: 
     yield [item] 

arr = [1,2,3,4,5] 
for item in get_next_batch(arr): 
    print item 

Side Punkte. Lassen Sie Ihre Klassen immer von object in Python 2.7 erben, und heben Sie nicht StopIteration an, um einen Generator in Python anzuhalten - es ist veraltetes Verhalten, das zu Fehlern führen kann. Verwenden Sie stattdessen return. z.B.

def get_next_batch(my_list): 
    for item in my_list: 
     if item > 3: 
      return 
     yield [item] 

batched = list(get_next_batch([1,2,3,4,5])) 
expected = [[1], [2], [3]] 
print batched == expected 

Sie aufräumen können Option1 es einfach zu bedienen in for-Schleifen zu machen. Um dies zu tun, können Sie es mit dem Iterator-Protokoll machen. Das ist eine __iter__ Methode, die self und eine next Methode zurückgibt, um das nächste Element zu erhalten. z.B.

class UsingNext(object): 

    def __init__(self, mylist): 
     self.current = 0 
     self.mylist = mylist 

    def __iter__(self): 
     return self 

    def next(self): # __next__ in python 3 
     if self.current >= len(self.mylist): 
      raise StopIteration 
     if self.current == 2: 
      self.current += 1 
      return "special" 
     item = self.mylist[self.current] 
     self.current += 1 
     return [item] 

class UsingYield(object): 

    def __init__(self, mylist): 
     self.mylist = mylist 

    def __iter__(self): 
     for current, item in enumerate(self.mylist): 
      if current == 2: 
       yield "special" 
       continue 
      yield [item] 

arr = range(1, 6) 
# both print 
# [1] 
# [2] 
# special 
# [4] 
# [5] 
for item in UsingNext(arr): 
    print item 
for item in UsingYield(arr): 
    print item 

In meinen Augen ist die Generatorversion sauberer und einfacher zu verstehen.

+0

Danke für Bemerkungen über Generator. Ich brauche Strom, denn wenn Logik komplizierter ist, erstelle ich Listen unterschiedlicher Größe. Gibt es ein Szenario, in dem eine Logik mit 'Option1' und nicht mit' Option2' implementiert werden kann? Zum Beispiel, wenn das nächste Item zurückkommt, hängt es von Items ab, die ich schon zurückgegeben habe oder so ähnlich? – Farseer

+1

Nein, beide können genau die gleichen Dinge erreichen. Es ist konzeptionell einfacher (meiner Meinung nach) Iteratoren mit Generatoren zu modellieren. – Dunes

+1

Ein wenig hinzugefügt, wie Option1 in einer for-Schleife arbeiten kann. – Dunes

Verwandte Themen