Python基礎 オブジェクト指向4(Iterator) 振る舞いに関するデザインパターン
| 登録日 | :2025/09/23 09:23 |
|---|---|
| カテゴリ | :Python基礎 |
Iteratorとは
コレクションの内部構造を利用者に見せずに、その要素に順番にアクセスする方法を提供するパターン
(コレクション:配列や辞書などのデータをまとめて格納するもの)
ループ処理のインデックスの役割を抽象化し一般化したもの
振る舞いに関するデザインパターン
Iteratorの構成要素
Iterator
- コレクションを探索するために必要な操作を定義するインターフェース
(has_next(), next()メソッドなどをもつ)
ConcreteIterator
- Iteratorで定義したメソッドを実装するクラス
- ConcretaIterator の実装によって探索の内容を変更することができる
- 探索を行うコレクションを属性にもつ
Aggregate
- 探索を行うコレクションを表すインターフェース
- Iteratorを生成するためのメソッドを定義
ConcreteAggregate
- Aggregateで定義したメソッドを実装するクラス
- ConcreteIteratorクラスの新しいインスタンスを返却する
Iteratorメソッド・デメリット
メリット
- 利用者がコレクションの詳細なデータ構造を知る必要がなくなる
-
コレクションの実装と探索のためのアルゴリズムを分離することができる
-
既存のコードに修正を加えることなく、新しい種類のコレクションやイテレータを追加できる
デメリット
- 単純なコレクションの場合、Iteratorを使用しない方がコードがシンプルになる
Iteratorの使い所
コレクションが複雑なデータ構造をしており、その複雑さを利用者から隠したい場合
(Iteratorのメソッドを呼び出すだけでデータを取得可能)
探索のための方法を複数持たせたい場合
(オープンクローズドの原則に違反することなく探索のためのロジックを追加可能)
サンプルコード
from abc import ABCMeta, abstractmethod
from typing import List
class Patient(object):
def __init__(self, _id: int, _name: str):
self.id = _id
self.name = _name
def __str__(self):
return self.name
class IIterator(metaclass=ABCMeta):
@abstractmethod
def has_next(self) -> bool:
pass
@abstractmethod
def next(self):
pass
class Aggregate(metaclass=ABCMeta):
@abstractmethod
def get_iterator(self) -> IIterator:
pass
class WaitingRoom(Aggregate):
"""ConcreteAggregate"""
def __init__(self):
self.__patients = []
def get_patients(self) -> List[Patient]:
return self.__patients
def get_count(self) -> int:
return len(self.__patients)
def check_in(self, _patient: Patient):
self.__patients.append(_patient)
def get_iterator(self) -> IIterator:
return WaitingRoomIterator(self)
class WaitingRoomIterator(IIterator):
"""ConcreteIterator"""
def __init__(self, _aggregate: WaitingRoom):
self.__position = 0
self.__aggregate = _aggregate
def has_next(self) -> bool:
return self.__position < self.__aggregate.get_count()
def next(self):
if not self.has_next():
print("[INFO]:There is no patient.")
return
patient = self.__aggregate.get_patients()[self.__position]
self.__position += 1
return patient
if __name__ == "__main__":
waiting_room = WaitingRoom()
waiting_room.check_in(Patient(1, "Yamada"))
waiting_room.check_in(Patient(2, "Suzuki"))
waiting_room.check_in(Patient(3, "Tanaka"))
iterator = waiting_room.get_iterator()
# print(iterator.next())
# print(iterator.next())
# print(iterator.next())
# print(iterator.next())
while iterator.has_next():
patient = iterator.next()
print(patient)
print(iterator.next())
pythonらしく書き直すと、__iter__や__next__を使うことで以下のように書くこともできる。こうすることで、pythonでfor文で使えるなどのメリットがある
class Patient:
def __init__(self, id, name):
self.id = id
self.name = name
def __repr__(self):
return f"Patient({self.id}, '{self.name}')"
class WaitingRoom:
def __init__(self):
self._patients = []
def check_in(self, patient):
self._patients.append(patient)
def __iter__(self):
return WaitingRoomIterator(self._patients)
class WaitingRoomIterator:
def __init__(self, patients):
self._patients = patients
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index < len(self._patients):
patient = self._patients[self._index]
self._index += 1
return patient
else:
raise StopIteration
# サンプルテスト
if __name__ == "__main__":
waiting_room = WaitingRoom()
waiting_room.check_in(Patient(1, "Yamada"))
waiting_room.check_in(Patient(2, "Suzuki"))
waiting_room.check_in(Patient(3, "Tanaka"))
for patient in waiting_room:
print(patient)
- 補足
サンプル2では、repr(self)を用いているが、目的は__str__(self)と近い。repr(self)の方が、より詳細な情報をprintで表示できる。