8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonで、デザインパターン「Iterator」を学ぶ

Last updated at Posted at 2020-01-27

GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。

■ Iterator(イテレータ・パターン)

Iteratorパターン(イテレータ・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。

UML class and sequence diagram

W3sDesign_Iterator_Design_Pattern_UML.jpg

UML class diagram

iterator.png
(以上、ウィキペディア(Wikipedia)より引用)

□ 備忘録

Iteratorパターンとは、何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのものだそうです。Iteratorは、日本語で、反復子と呼ばれることもあるそうです。
Pythonプログラミングに携わっていると、よくお目にかかるやつですね。

■ "Iterator"のサンプルプログラム

実際に、Iteratorパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

  • 本棚に、Aroun d the World in 80 daysの本を追加する
  • 本棚に、Bibleの本を追加する
  • 本棚に、Cinderellaの本を追加する
  • 本棚に、Daddy-Long-Legsの本を追加する
  • 現在、本棚に存在する本のタイトルを表示する
$ python Main.py 
Aroun d the World in 80 days
Bible
Cinderella
Daddy-Long-Legs

■ サンプルプログラムの詳細

Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Iterator/step1

  • ディレクトリ構成
.
├── Main.py
└── iterator
    ├── __init__.py
    ├── aggregate.py
    ├── book.py
    └── iterator.py

(1) Iterator(反復子)の役

要素を順番にスキャンしていくインタフェースを定める役です。
サンプルプログラムでは、Iteratorクラスが、この役を努めます。

iterator/iterator.py
from abc import ABCMeta, abstractmethod

class Iterator(metaclass=ABCMeta):
    @abstractmethod
    def hasNext(self):
        pass

    @abstractmethod
    def next(self):
        pass

(2) ConcreteIterator(具体的な反復子)の役

Iterator役で定めたインタフェースを実際に実装する役です。
サンプルプログラムでは、BookShelfIteratorクラスが、この役を努めます。
この役は、スキャンするために必要な情報を持っている必要があります。
サンプルプログラムでは、BookShelfクラスのインスタンスをインスタンス変数self.__bookShelfで覚えており、注目している本をインスタンス変数self.__indexで覚えるようになっています。

iterator/book.py
from iterator.iterator import Iterator

...(snip)

class BookShelfIterator(Iterator):
    def __init__(self, bookShelf):
        self.__bookShelf = bookShelf
        self.__index = 0

    def hasNext(self):
        return True if self.__index < self.__bookShelf.getLength() else False

    def next(self):
        book = self.__bookShelf.getBookAt(self.__index)
        self.__index += 1
        return book

(3) Aggregate(集合体)の役

Iterator役を作り出すインタフェースを定める役です。そのインタフェースは、「私が持っている要素を順番にスキャンしてくれる人」を作り出す抽象化メソッドということになります。
サンプルプログラムでは、Aggregateクラスが、この役を努めます。

iterator/aggregate.py
from abc import ABCMeta, abstractmethod

class Aggregate(metaclass=ABCMeta):
    @abstractmethod
    def iterator(self):
        pass

(4) ConcreteAggregate(具体的な集合体)の役

Aggregate役が定めたインタフェースを実際に実装する役です。具体的なIterator役、すなわちConcreteIterator役のインスタンスを作り出します。
サンプルプログラムでは、BookShelfクラスが、この役を努めます。

iterator/book.py
from iterator.aggregate import Aggregate

...(snip)

class BookShelf(Aggregate):
    def __init__(self, maxSize):
        self.__last = 0
        self.__books = [None] * maxSize

    def getBookAt(self, index):
        return self.__books[index]

    def append(self, book):
        self.__books[self.__last] = book
        self.__last += 1

    def getLength(self):
        return self.__last

    def iterator(self):
        return BookShelfIterator(self)

(5) Client(依頼人)の役

サンプルプログラムでは、startMainメソッドが、この役を努めます。

Main.py
from iterator.book import Book, BookShelf

def startMain():
    bookShelf = BookShelf(4)
    bookShelf.append(Book(name="Aroun d the World in 80 days"))
    bookShelf.append(Book(name="Bible"))
    bookShelf.append(Book(name="Cinderella"))
    bookShelf.append(Book(name="Daddy-Long-Legs"))
    it = bookShelf.iterator()
    while it.hasNext():
        book = it.next()
        print(book.getName())

if __name__ == '__main__':
    startMain()

(6)その他

本のタイトルを管理します。

iterator/book.py
class Book(object):
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name

□ 備忘録 (Pythonイテレータを活用してみる!)

Pythonプログラミングで、よく、イテレータを見かける機会が多いです。
Pythonイテレータについては、こちらのWeb記事「[Python入門]イテレータとは」を参考になります。
なお、新たなクラスを定義する際に、イテレータの性質を備えるためには、次の要件を満たす必要があるそうです。

  • 自分自身を戻り値とする__iter__メソッドを持つ
  • 自分が管理している要素列の値を1つずつ返す__next__メソッドを持つ
  • __next__メソッドでは、要素が尽きたらStopIteration例外を発生する

サンプルプログラムをPythonイテレータで書き換えてみたいと思います。
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Iterator/step2

iterator/book.py
class Book(object):
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name


class BookShelf(object):
    def __init__(self):
        self.__books = []

    def append(self, book):
        self.__books.append(book)

    def __iter__(self):
        self.__index = 0
        return self

    def __next__(self):
        if self.__index >= len(self.__books):
            raise StopIteration()
        book = self.__books[self.__index]
        self.__index += 1
        return book
Main.py
from iterator.book import Book, BookShelf

def startMain():
    bookShelf = BookShelf()
    bookShelf.append(Book(name="Aroun d the World in 80 days"))
    bookShelf.append(Book(name="Bible"))
    bookShelf.append(Book(name="Cinderella"))
    bookShelf.append(Book(name="Daddy-Long-Legs"))
    for book in bookShelf:
        print(book.getName())

if __name__ == '__main__':
    startMain()

だいぶ、シンプルになりました。
では、動かしてみます。

$ python Main.py 
Aroun d the World in 80 days
Bible
Cinderella
Daddy-Long-Legs

最初の動作結果と同じになりましたので、これで、ひとまず、完成とします。

■ 参考URL

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?