6.14 モジュールではなくクラスとオブジェクトを使うべきなのはいつか
プログラムを構築する際、コードをクラスにまとめるべきか、モジュールにまとめるべきか迷うことがあるかもしれません。ここでは、その判断基準となるガイドラインを紹介します。
クラスとモジュールの使い分けガイドライン
Section titled “クラスとモジュールの使い分けガイドライン”- 複数のインスタンスが必要な場合は「クラス」: 動作(メソッド)は同じでも、内部状態(属性)がそれぞれ異なる複数のオブジェクト(インスタンス)を必要とする場合は、クラスがもっとも適しています。
- 継承が必要な場合は「クラス」: クラスは継承をサポートしますが、モジュールはサポートしません。
- ひとつだけ必要な場合は「モジュール」: プログラム内で唯一の存在(シングルトン)で十分な場合は、モジュールが適しています。Pythonモジュールは何度インポートされても、1つのコピーしかロードされません。
- 複数の値と関数をまとめる場合は「クラス」: 複数の値を持つ変数を、複数の関数に引数として引き回しているような場合、それらを属性とメソッドとして1つのクラスにまとめた方がコードがすっきりします。
- もっとも単純な方法を選ぶ: 辞書、リスト、タプルは、モジュールよりも単純で高速であり、クラスよりも普通は単純です。問題に対して過剰な構造を作り込まないようにしましょう。
Guido のアドバイス データ構造を作り込みすぎないようにしよう。オブジェクトよりもタプルの方がいい(名前付きタプルも試してみよう)。ゲッター/セッター関数よりも単純なフィールドを選ぶようにしよう。組み込みデータ型はプログラマーの友達だ。数値、文字列、タプル、リスト、集合、辞書をもっと使おう。そして、コレクションライブラリ、特にデックをチェックしよう。 — Guido van Rossum
6.14.1 名前付きタプル (namedtuple)
Section titled “6.14.1 名前付きタプル (namedtuple)”Guidoも推奨している名前付きタプルは、タプルのサブクラスであり、位置(インデックス)だけでなく名前(.name)でも値にアクセスできる非常に便利なデータ構造です。
名前付きタプルは自動的に用意されるわけではないため、collections モジュールからインポートして使います。
名前付きタプルの作成
Section titled “名前付きタプルの作成”namedtuple 関数に、名前と空白区切りのフィールド名文字列を渡して定義します。
from collections import namedtuple
# 'Duck'という名前の、'bill'と'tail'というフィールドを持つ名前付きタプルを定義Duck = namedtuple('Duck', 'bill tail')
# 値を渡してオブジェクトを作成duck = Duck('wide orange', 'long')
print(duck)# 出力: Duck(bill='wide orange', tail='long')
# ドット記法でフィールド名を使ってアクセスできるprint(duck.bill) # wide orangeprint(duck.tail) # long辞書からの作成(キーワード引数の展開)
Section titled “辞書からの作成(キーワード引数の展開)”辞書に格納されているデータを **(キーワード引数展開)を使って渡すことで、簡単に名前付きタプルを作成できます。
parts = {'bill': 'wide orange', 'tail': 'long'}
# 辞書のキーと値を展開して渡すduck2 = Duck(**parts)
print(duck2)# 出力: Duck(bill='wide orange', tail='long')値の変更(新しいタプルの生成)
Section titled “値の変更(新しいタプルの生成)”名前付きタプルはタプルの一種なのでイミュータブル(変更不可)ですが、_replace() メソッドを使うことで、一部のフィールドを置き換えた新しい名前付きタプルを作ることができます。
# tailとbillの値を変更した新しいオブジェクトを生成duck3 = duck2._replace(tail='magnificent', bill='crushing')
print(duck3)# 出力: Duck(bill='crushing', tail='magnificent')