はじめに
こんにちは。孔子の80代目子孫兼AS部DS2課の孔です。突然ですが、初めてステーキを食べた時の感動、覚えていますか?今まで肉といえばサムギョプサルが一番美味いと思っていた少年時代の私は、ステーキを初めて食べた中学2年生の時の感動をいまだに忘れられません。世の中にはこんなに素敵な食べ物があるんだ、ステーキなだけに…
ということで、まだデータの集まりを取り扱うときにListしか使ってない人には目から鱗、初めてステーキを食べたとき並みの感動をお届けできる、collectionsライブラリについて説明したいと思います。Pythonにはデータの集まりを処理するために、デフォルトで以下の型を提供しています。
- list
- dict
- set
- tuple
もちろんこちらの型もとても汎用的で優れたものになりますが、基本的な型となるのでどうしても汎用性を重視しています。そこで、より用途が明確なときにより優れた代案になれる型がcollectionライブラリに用意されています。百聞は一見に如かず、それではみてみましょう!
まずCollectionsのドキュメントはこちら
何がともあれ、開発者にとって最も大事なのはドキュメントです。collectionsのドキュメントは以下のものになります。
collections --- コンテナデータ型 — Python 3.10.6 ドキュメント
ドキュメントを読むと、以下のような文言が書かれています。
このモジュールは、汎用の Python 組み込みコンテナ dict, list, set, および tuple に代わる、特殊なコンテナデータ型を実装しています。
コンテナデータ型…?とはてなになる方もいらっしゃるかと思いますが、コンテナデータ型というのはデータの集まりを取り扱う型を言います。メモリを空けといて、その中にデータを積んでいくのがコンテナと似てるので、そのような名前になっています(例えば、appendメソッドでリストにデータをどんどん積んでいくイメージです)このコンテナデータ型の別名がCollectionです(本モジュールの名前でもあります!)
要するに、Collection = コンテナデータ型は、データの集まりを取り扱う型になります(組み込みで提供しているlist, dictなどもコンテナデータ型になります)
それでは、有用な型を一個ずつみていきましょう!
namedtuple:名前がついてるtuple
命名通り、各要素に名前がついてるtuple型が「namedtuple」です。パッとみてイメージができないかもしれませんが、以下の例をみてみましょう。
from collections import namedtuple Employee = namedtuple('Employee', [ 'name', 'age', 'department', ]) kong = Employee('kong', 18, 'ds_2') kong.name # 'kong' kong.age # 18 kong.department # ds_2
namedtupleを使うと、このように各要素に名前(name, age, department)を与えて、その要素名からデータにアクセスすることができます。namedtupleにはいろいろなメリットがありますが、代表的には以下の2つのメリットがあります。
- パッとみてどんな構造なのか分かり易い(=可読性がいい)
- tuple同様データの再代入ができないので、安全に使える
そのメリットを知るために、同じく名前でデータにアクセスできるdict型と比較してみましょう!
Employee = { 'name': '', 'age': 0, 'department': '', } kong = Employee.copy() kong['name'] = 'kong' kong['age'] = 18 kong['department'] = 'ds_2' kong['office'] = 'tokyo' # 開発者の意図から外れて、必要以上に項目を入れることができちゃう kong['age'] = 90 # データを再代入することができちゃう
とある型(Employee)がどんな項目を持つべきものなのかを明確に定義し、一度作成したデータがいつの間にかすり替わって想定した結果と異なる挙動になるようなことを防げるのがnamedtupleのメリットです。
ただし、データにアクセスするコストはnamedtupleよりdictが早いです。大量のデータを生成してそれぞれにアクセスしたりするようなケースではパフォーマンスを考慮して選択する必要があります。
もっと実用的に使うためのTips
namedtupleには複数の要素が入るため、安全に利用するには型ヒントが必要になることが多いです。 型ヒントは以下の形で定義することができます。
# 方法1 from collections import namedtuple Employee = namedtuple('Employee', [ ('name', str), ('age', int), ('department', str), ]) # 方法2 import typing class Employee(typing.NamedTuple): name: str age: int department: str
先ほど紹介したcollections
ライブラリからの定義の他、typing
のNamedTuple
を継承して作成することもできるので、ご参照ください。
参考までにですが、「方法2」似たような書き方でほぼ同様の実装ができる方法として、dataclass
を使った実装もあります。
from dataclasses import dataclass @dataclass class Employee: name: str age: int department: str
namedtuple
とdataclass
はやってることはほぼ同じですが、Pythonの実装方法が異なります。
namedtuple
は名前通りtuple
を元に実装していて、dataclass
はdict
を元に実装されています。
そのため、メモリ面ではnamedtuple
の方がメリットがありますが、データへのアクセススピーと面ではdataclass
の方が有利だったりします。
最後に
今回はtuple
の拡張版である、namedtuple
を見てみました。いろいろなメリットがあるかと思いますが、一言でまとめると
「イミュータブル & dict的なものが欲しい」
時に第一候補としてnamedtuple
を考慮いただけたら良いかと思います。次回はOrderedDict
を紹介しようと思います、またよろしくお願いします!
参考資料
- collections
- namedtuple
- dataclass