web-dev-qa-db-ja.com

Pythonイテレータとインデックスによってアクセス可能なクラス

N00bの質問かもしれませんが、私は現在、イテレータを実装するクラスを持っているので、次のようなことができます

for i in class():

しかし、私は同様にインデックスでクラスにアクセスできるようにしたい

class()[1]

どうやってやるの?

ありがとう!

43
xster

__iter__()__getitem__() などのメソッドの両方を実装します。

@Ignacio Vazquez-Abramsからの現在の 受け入れられた回答 で十分です。ただし、この質問に興味のある人は、 abstract base class(ABC標準モジュール_collections.abc_ )。これは多くのことを行います( おそらく他にもあります ):

  • オブジェクトを「____」のように扱うために必要なすべてのメソッドがそこにあることを保証します
  • コードを読んでいる人は、オブジェクトが「____のように振る舞う」ことを意図していることを即座に知ることができるため、自己文書化されます。
  • isinstance(myobject,SomeABC)が正しく機能するようにします。
  • 多くの場合、メソッドを自動的に提供するため、自分で定義する必要はありません

(上記に加えて、独自のABCを作成すると、オブジェクト内の特定のメソッドまたはメソッドセットの存在をテストし、これに基づいてそのオブジェクトを宣言することができます。 ABCのサブクラスであるオブジェクトがABCdirectlyを継承しない場合でもこの回答を参照詳細については。


例:listを使用して、読み取り専用のABCのようなクラスを実装する

例として、元の質問のクラスにABCを選択して実装しましょう。次の2つの要件があります。

  1. クラスは反復可能です
  2. インデックスでクラスにアクセスします

明らかに、このクラスはある種のコレクションになります。そこで、 collection ABCのメニュー を見て適切なABCを見つけます( numeric ABCs もあります)。適切なABCは、クラスで使用する抽象メソッドによって異なります。

メソッド__iter__()を使用したい場合、 Iterable が後のものであることがわかります。これは、_for o in myobject:_のようなことを行うために必要なものです。ただし、Iterableにはメソッド__getitem__()が含まれません。これは_myobject[i]_のようなことを行うために必要なものです。したがって、別のABCを使用する必要があります。

抽象基本クラスの_collections.abc_メニューを見ると、 Sequence が必要な機能を提供する最も単純なABCであることがわかります。そして-あなたはそれを見ていただけますか-Iterable機能をmixinメソッドとして取得します-つまり、自分で定義する必要はありません-無料です!また、___contains___、___reversed___、index、およびcountも取得します。考えてみると、これはすべてのshouldがインデックス付きオブジェクトに含まれているものです。あなたがそれらを含めるのを忘れていたなら、あなたのコードのユーザー(潜在的にあなた自身を含む!)はかなりイライラするかもしれません(私はそうするでしょう)。

ただし、2番目のABCもあり、この機能の組み合わせ(反復可能で、_[]_でアクセス可能)があります: Mapping 。どちらを使用しますか?

要件はオブジェクトにアクセスできることであることを思い出してくださいby indexlistTupleのような)、つまりnotby keydictのような)。したがって、SequenceではなくMappingを選択します。


補足:Sequenceは読み取り専用(Mappingと同様)であるため、_myobject[i] = value_やrandom.shuffle(myobject)のようなことはできません。そのようなことができるようにしたい場合は、ABCsのメニューを続行し、 MutableSequence (または MutableMapping )を使用する必要があります。メソッド。


サンプルコード

これでクラスを作成できます。定義し、Sequenceから継承します。

_from collections.abc import Sequence

class MyClass(Sequence):
    pass
_

それを使用しようとすると、インタープリターは使用する前に実装する必要があるメソッドを教えてくれます(メソッドはPython docsページにもリストされていることに注意してください):

_>>> myobject = MyClass()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods __getitem__, __len__
_

これは、先に進んで___getitem___および___len___を実装すると、新しいクラスを使用できるようになることを示しています。 Python 3:

_from collections.abc import Sequence

class MyClass(Sequence):
    def __init__(self,L):
        self.L = L
        super().__init__()
    def __getitem__(self, i):
        return self.L[i]
    def __len__(self):
        return len(self.L)

# Let's test it:
myobject = MyClass([1,2,3])
try:
    for idx,_ in enumerate(myobject):
        print(myobject[idx])
except Exception:
    print("Gah! No good!")
    raise
# No Errors!
_

動作します!

69
Rick Teachey