2016-07-26 2 views
1

コンテナ用のカスタムラッパークラスを作成しようとしています。イテレータ-prototocolを実装するために私が__iter____next__を提供し、私は__getitem__を提供し、個々の項目にアクセスするために:__iter__と__getitem__を実装しているコンテナのカスタムラッパー

#!/usr/bin/python 
# -*- coding: utf-8 -*- 
from __future__ import absolute_import, division, print_function, unicode_literals, with_statement 
from future_builtins import * 
import numpy as np 

class MyContainer(object): 

    def __init__(self, value): 
     self._position = 0 
     self.value = value 

    def __str__(self): 
     return str(self.value) 

    def __len__(self): 
     return len(self.value) 

    def __getitem__(self, key): 
     return self.value[key] 

    def __iter__(self): 
     return self 

    def next(self): 
     if (self._position >= len(self.value)): 
      raise StopIteration 
     else: 
      self._position += 1 
      return self.value[self._position - 1] 

することはこれまでのところ、すべてのものは、電子、期待通りに動作します。 g。以下のようなものをしようとしたとき:

if __name__ == '__main__': 
    a = MyContainer([1,2,3,4,5]) 
    print(a) 
    iter(a) is iter(a) 
    for i in a: 
     print(i) 
    print(a[2]) 

しかしnumpy.maximumを使用しようとすると、私は問題に実行:

b= MyContainer([2,3,4,5,6]) 
np.maximum(a,b) 

は "ValueError: cannot copy sequence with size 5 to array axis with dimension 0" を発生させます。

__iter__方法をコメントアウトするとき(もはやイテレータプロトコルに準拠しながら)、Iはバック正しい結果とnumpyの配列を取得していない:

print(np.maximum(a,b)) # results in [2 3 4 5 6] 

そして__getitem__をコメントアウトするとき、私はインスタンスを取り戻しますMyContainer

しかし、私は個々のアイテムへのアクセスを失います。

すべての3つの目標を一緒に達成する方法はありますか(Iterator-Protocol、__getitem__およびnumpy.maximum)。基本的に間違っていることはありますか?

メモ:実際のラッパークラスにはもっと多くの機能がありますが、これは動作を再現できる最小限の例です。

(パイソン2.7.12、numpyの1.11.1)

+1

コンテナが独自の(そして唯一の)イテレータなのはなぜですか? – user2357112

+0

'np.array(MyContainer)'とは何ですか? np.mamimumは配列に対して機能します。 – hpaulj

+0

@hpaulj "print(np.array(MyContainer))"の結果は、 " __main __。MyContainer '>"、 "print(np.array(MyContainer([1,2,3,4,5])))上記のValueErrorと同じ結果になります。 – grma0025

答えて

1

あなたの容器が大きく、それを制限する独自のイテレータです。各コンテナは一度しか反復することができません。その後、反復プロトコルが実行される限り、「空」とみなされます。

見るためにあなたのコードでこれを試してみてください:

c = MyContainer([1,2,3]) 
l1 = list(c) # the list constructor will call iter on its argument, then consume the iterator 
l2 = list(c) # this one will be empty, since the container has no more items to iterate on 

あなたが__iter__方法を提供するが、__len__ methondと小さな整数のインデックスを受け入れる__getitem__メソッドを実装しません場合は、Pythonは反復する__getitem__を使用します。 。このような反復は、作成される反復子オブジェクトがすべて互いに異なるため、複数回実行できます。

__iter__メソッドをクラスから取り除いた後、上記のコードを実行すると、両方のリストが期待通りに[1, 2, 3]になります。独自のイテレータを返すように独自の__iter__メソッドを修正することもできます。たとえば、あなたはあなたの内部配列からイテレータを返すことができます:

def __iter__(self): 
    return iter(self.value) 

あるいは、Bakuriuによってコメントで提案されているように:

def __iter__(self): 
    return (self[i] for i in range(len(self)) 

この後者のバージョンを使用する場合はPythonがあなたのために提供されるもの、本質的です__getitem__方法を有するが、__iter__方法を有さない。

関連する問題