2016-04-11 12 views
0

以下の2つのオプションから、イテラブルを実行するための魅力的なもの(可読性の高いコード、より多くのpythonic、効率性など)と将来のロジックを追加したい場合1からすべての戻り値)?リターンジェネレータVS繰り返し実行中なしなし

オプションの1つはジェネレータを返し、もう1つは次のアイテムを返し、すべての繰り返しを実行するとNoneを返します。

好ましい方法はありますか?はいの場合、なぜですか?他にはない方法がいくつかあります。

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

使用法:どちらの場合も

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 

出力:あなたはほぼ確実秒で行きたい

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

答えて

1

。ラインが少なく、物事が間違ってしまう機会が少なくなります。

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 

サイドポイント:

しかし、あなたはイテレータの外currentを使用していないとして、私はちょうどダウンに全体を最適化します見て。あなたのクラスは常にobjectのpython 2.7から継承させ、StopIterationを生成してpythonでジェネレータを停止させないでください。バグにつながる可能性のある非推奨の動作です。代わりにreturnを使用してください。例えば。

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 

あなたは、forループで使用することを容易にするためにOption1を整理することができます。これを行うには、反復子プロトコルを使用して作成できます。これは__iter__メソッドで、次の項目を取得する方法はselfnextです。例えば。

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 

私の考えでは、発電機のバージョンはよりクリーンでわかりやすいです。

+0

発電機に関するご意見ありがとうございます。ロジックが複雑なときは、異なるサイズのリストを生成するので、私は現在が必要です。いくつかのロジックが 'Option1'を使って実装され、' Option2'は使われないシナリオがありますか?たとえば、次回の商品返品は、すでに返品された商品やそのようなものに依存しますか? – Farseer

+1

いいえ、どちらもまったく同じことを達成できます。概念的には、ジェネレータを使ってイテレータをモデル化する方が簡単です(私の意見では)。 – Dunes

+1

forループでOption1を動作させる方法を少し追加しました。 – Dunes