【Python】collectionsライブラリを活用しよう_1. namedtuple

記事タイトルとURLをコピーする

はじめに

こんにちは。孔子の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ライブラリからの定義の他、typingNamedTupleを継承して作成することもできるので、ご参照ください。 参考までにですが、「方法2」似たような書き方でほぼ同様の実装ができる方法として、dataclassを使った実装もあります。

from dataclasses import dataclass
 
@dataclass
class Employee:
    name: str
    age: int
    department: str

namedtupledataclassはやってることはほぼ同じですが、Pythonの実装方法が異なります。 namedtupleは名前通りtupleを元に実装していて、dataclassdictを元に実装されています。 そのため、メモリ面ではnamedtupleの方がメリットがありますが、データへのアクセススピーと面ではdataclassの方が有利だったりします。

最後に

今回はtupleの拡張版である、namedtupleを見てみました。いろいろなメリットがあるかと思いますが、一言でまとめると

「イミュータブル & dict的なものが欲しい」

時に第一候補としてnamedtupleを考慮いただけたら良いかと思います。次回はOrderedDictを紹介しようと思います、またよろしくお願いします!

参考資料

「【Python】collectionsライブラリを活用しよう」シリーズ一覧

孔 允培 (執筆記事の一覧)

アプリケーションサービス部 ディベロップメントサービス課

孔子の80代目子孫