Help us understand the problem. What is going on with this article?

Pythonでファーストクラスコレクションを生成する

はじめに

オブジェクト指向を勉強していると、よく
「同じ型の複数のオブジェクトを扱うロジックはファーストクラスコレクションにまとめる」
というような文章を目にするかと思います。
これまで、具体的にpythonでどのように書けばいいか分からなかったのですが、
自分なりに「こう書けばすっきり分かりやすくなりそうだ」という記述ができたので記事にしました。

とりあえず書いてみる

商品クラスとそのファーストクラスコレクション(以下コレクションと記載する)を考えてみます。

import dataclasses
from typing import List

@dataclasses.dataclass(frozen=True)
class Item(object):
    name: str
    price: int

@dataclasses.dataclass(frozen=True)
class ItemCollection(object):
    # default_factoryで何の型で初期化するかを指定できます
    items: List[Item] = dataclasses.field(default_factory=list)

    def add_item(self, item):
        # 要素を追加する際は新しいオブジェクトを生成して返します。
        # 既存のコレクションに副作用を与えずに要素を追加した新規オブジェクトを生成する方法が
        # これ以外に思い浮かばなかった・・・
        return ItemCollection(self.items + [item])

dataclassesの基本的な使い方はこちらも読んでいただければと・・・
(https://qiita.com/madogiwa_429/items/55c78fc6be7887a28df8)

とりあえず、これでファーストクラスコレクションが作れます。

ファーストクラスコレクションへの疑念

これまで、コレクションに対して不便だと思っていた理由に、
「簡単に書けるはずの処理の記述が面倒になる」というのがありました。
例えば、今回の例で、「コレクションに格納されている商品の合計金額を求めたい」というとき、
どのように書けばいいでしょう。
int型でリストに入れていればsum関数で書けるものが、
Itemクラスを格納したことで簡潔に記述できなくなってしまいます。
こういうこともあり、配列を扱う場合に本当にオブジェクト指向で書くのが良いのか分からずにいました。

map関数を使う

この疑念に一定の解を与えてくれるのがmap関数です。
map関数はリストなどのイテラブルオブジェクトの各要素に対して処理を行ってくれます。
map関数を使うと、コレクションに格納されている商品の合計金額を求めるメソッドは以下のように書けます。

@dataclasses.dataclass(frozen=True)
class ItemCollection(object):
    items: List[Item] = dataclasses.field(default_factory=list)

    def add_item(self, item):
        return ItemCollection(self.items + [item])

    def sum_price(self):
        # itemsの各要素のインスタンス変数"price"の値だけのリストができる
        price_list = list(map(lambda item: item.price, self.items))
        return sum(price_list)        

まとめ

map関数を使うことで、ファーストクラスコレクションのメソッドを簡潔に書けました。
Pythonでオブジェクト指向を書くときに同じ悩みを持っている方の参考になれば幸いです。

ちなみに・・・

frozen=Trueとすることで、確かにインスタンス変数の再代入は防げるのですが、
リストの要素追加は防げませんでした・・・

test = ItemCollection()

# この処理はエラーになる
test.items = ['てすと']

# この処理はエラーにならず、要素が追加されてしまう
test.items.append('てすと')
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした