Python3でコレクションモジュールを使用する方法
著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
Python 3には、タプル、辞書、リストなど、多数の組み込みデータ構造があります。 データ構造は、データを整理して保存する方法を提供します。 collections
モジュールは、データ構造の入力と操作を効率的に行うのに役立ちます。
このチュートリアルでは、コレクションモジュールの3つのクラスを実行して、タプル、辞書、およびリストの操作を支援します。 namedtuples
を使用して名前付きフィールドを持つタプルを作成し、defaultdict
を使用して辞書内の情報を簡潔にグループ化し、deque
を使用してリストのようなオブジェクトの両側に要素を効率的に追加します。
このチュートリアルでは、主に、架空の水族館に魚を追加したり、水族館から魚を削除したりするときに変更する必要のある魚の在庫を扱います。
前提条件
このチュートリアルを最大限に活用するには、タプル、ディクショナリ、およびリストのデータ型について、構文とそれらからデータを取得する方法の両方についてある程度理解しておくことをお勧めします。 必要な背景情報については、次のチュートリアルを確認できます。
タプルへの名前付きフィールドの追加
Pythonタプルは、不変または不変の順序付けられた要素のシーケンスです。 タプルは、列データを表すために頻繁に使用されます。 たとえば、CSVファイルの行やSQLデータベースの行などです。 水族館は、一連のタプルとして魚の在庫を追跡する場合があります。
個々の魚のタプル:
("Sammy", "shark", "tank-a")
このタプルは、3つの文字列要素で構成されています。
このタプルはいくつかの点で便利ですが、各フィールドが何を表しているかを明確に示していません。 実際には、要素0
は名前、要素1
は種、要素2
は貯蔵タンクです。
魚のタプルフィールドの説明:
名前 | 種族 | タンク |
---|---|---|
サミー | 鮫 | 短歌 |
この表は、タプルの3つの要素のそれぞれが明確な意味を持っていることを明確にしています。
collections
モジュールのnamedtuple
を使用すると、タプルの各要素に明示的な名前を追加して、Pythonプログラムでこれらの意味を明確にすることができます。
namedtuple
を使用して、魚のタプルの各要素に明確に名前を付けるクラスを生成してみましょう。
from collections import namedtuple Fish = namedtuple("Fish", ["name", "species", "tank"])
from collections import namedtuple
は、Pythonプログラムにnamedtuple
ファクトリ関数へのアクセスを提供します。 namedtuple()
関数呼び出しは、Fish
という名前にバインドされたクラスを返します。 namedtuple()
関数には、2つの引数があります。新しいクラスの目的の名前"Fish"
と名前付き要素のリスト["name", "species", "tank"]
です。
Fish
クラスを使用して、以前の魚のタプルを表すことができます。
sammy = Fish("Sammy", "shark", "tank-a") print(sammy)
このコードを実行すると、次の出力が表示されます。
OutputFish(name='Sammy', species='shark', tank='tank-a')
sammy
は、Fish
クラスを使用してインスタンス化されます。 sammy
は、明確に名前が付けられた3つの要素を持つタプルです。
sammy
のフィールドには、名前または従来のタプルインデックスを使用してアクセスできます。
print(sammy.species) print(sammy[1])
これらの2つのprint
呼び出しを実行すると、次の出力が表示されます。
Outputshark shark
.species
にアクセスすると、[1]
を使用してsammy
の2番目の要素にアクセスした場合と同じ値が返されます。
collections
モジュールのnamedtuple
を使用すると、タプルの重要なプロパティ(不変で順序付けられている)を維持しながら、プログラムが読みやすくなります。
さらに、namedtuple
ファクトリ関数は、Fish
のインスタンスにいくつかのメソッドを追加します。
._ asdict()を使用して、インスタンスを辞書に変換します。
print(sammy._asdict())
print
を実行すると、次のような出力が表示されます。
Output{'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}
sammy
で.asdict()
を呼び出すと、3つのフィールド名のそれぞれを対応する値にマッピングするディクショナリが返されます。
3.8より古いPythonバージョンでは、この行の出力が少し異なる場合があります。 たとえば、ここに示されている単純な辞書の代わりにOrderedDict
が表示される場合があります。
注: Pythonでは、先頭にアンダースコアが付いているメソッドは通常「プライベート」と見なされます。 namedtuple によって提供される追加のメソッド(_asdict()
、._make()
、。_replace()
など)。ただし、はパブリックです。
辞書にデータを収集する
多くの場合、Python辞書でデータを収集すると便利です。 collections
モジュールのdefaultdict
は、辞書内の情報をすばやく簡潔に組み立てるのに役立ちます。
defaultdict
はKeyError
を発生させません。 キーが存在しない場合、defaultdict
は代わりにプレースホルダー値を挿入して返します:
from collections import defaultdict my_defaultdict = defaultdict(list) print(my_defaultdict["missing"])
このコードを実行すると、次のような出力が表示されます。
Output[]
defaultdict
は、KeyError
をスローする代わりに、プレースホルダー値を挿入して返します。 この場合、プレースホルダー値をリストとして指定しました。
対照的に、通常の辞書は、欠落しているキーにKeyError
をスローします。
my_regular_dict = {} my_regular_dict["missing"]
このコードを実行すると、次のような出力が表示されます。
OutputTraceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'missing'
通常の辞書my_regular_dict
は、存在しないキーにアクセスしようとするとKeyError
を発生させます。
defaultdict
は通常の辞書とは動作が異なります。 defaultdict
は、欠落しているキーでKeyError
を発生させる代わりに、引数なしでプレースホルダー値を呼び出して、新しいオブジェクトを作成します。 この場合、list()
を使用して、空のリストを作成します。
架空の水族館の例を続けて、水族館の在庫を表す魚のタプルのリストがあるとしましょう。
fish_inventory = [ ("Sammy", "shark", "tank-a"), ("Jamie", "cuttlefish", "tank-b"), ("Mary", "squid", "tank-a"), ]
水族館には3匹の魚がいます。これらの3つのタプルには、名前、種、貯蔵タンクが記載されています。
私たちの目標は、水槽ごとに在庫を整理することです。各水槽にいる魚のリストを知りたいのです。 つまり、"tank-a"
を["Sammy", "Mary"]
に、"tank-b"
を["Jamie"]
にマップする辞書が必要です。
defaultdict
を使用して、水槽ごとに魚をグループ化できます。
from collections import defaultdict fish_inventory = [ ("Sammy", "shark", "tank-a"), ("Jamie", "cuttlefish", "tank-b"), ("Mary", "squid", "tank-a"), ] fish_names_by_tank = defaultdict(list) for name, species, tank in fish_inventory: fish_names_by_tank[tank].append(name) print(fish_names_by_tank)
このコードを実行すると、次の出力が表示されます。
Outputdefaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})
fish_names_by_tank
はdefaultdict
として宣言され、デフォルトでKeyError
をスローする代わりにlist()
を挿入します。 これにより、fish_names_by_tank
のすべてのキーがlist
を指すことが保証されるため、.append()
を自由に呼び出して、各タンクのリストに名前を追加できます。
defaultdict
は、予期しないKeyErrors
の可能性を減らすため、ここで役立ちます。 予期しないKeyErrors
を減らすことは、プログラムをより明確に、より少ない行で書くことができることを意味します。 より具体的には、defaultdict
イディオムを使用すると、すべてのタンクの空のリストを手動でインスタンス化する必要がなくなります。
defaultdict
がない場合、for
ループ本体は次のようになります。
defaultdictなしのより詳細な例
... fish_names_by_tank = {} for name, species, tank in fish_inventory: if tank not in fish_names_by_tank: fish_names_by_tank[tank] = [] fish_names_by_tank[tank].append(name)
(defaultdict
の代わりに)通常の辞書だけを使用するということは、for
ループ本体が常にfish_names_by_tank
に指定されたtank
の存在をチェックする必要があることを意味します。 tank
がfish_names_by_tank
にすでに存在すること、または[]
で初期化されたことを確認した後でのみ、魚の名前を追加できます。
defaultdict
は、KeyError
を発生させないため、辞書を埋めるときにボイラープレートコードを削減するのに役立ちます。
コレクションのいずれかの側に要素を効率的に追加するためのdequeの使用
Pythonリストは、変更可能または変更可能な順序付けられた要素のシーケンスです。 Pythonは一定時間でリストに追加できますが(リストの長さは追加にかかる時間に影響しません)、リストの先頭への挿入は遅くなる可能性があります。リストが大きくなるにつれて、かかる時間は長くなります。
Big O表記に関しては、リストへの追加は定数時間O(1)
操作です。 対照的に、リストの先頭への挿入は、O(n)
のパフォーマンスでは遅くなります。
注:ソフトウェアエンジニアは、「BigO」表記と呼ばれるものを使用して手順のパフォーマンスを測定することがよくあります。 入力のサイズが手順の実行にかかる時間に影響を与えない場合、定数時間またはO(1)
(「BigO of1」)で実行されると言われます。 上で学んだように、Pythonは、O(1)
とも呼ばれる、一定の時間パフォーマンスでリストに追加できます。
入力のサイズが、プロシージャの実行にかかる時間に直接影響する場合があります。 たとえば、Pythonリストの先頭に挿入すると、リストに含まれる要素が多いほど実行速度が遅くなります。 Big O表記では、文字n
を使用して入力のサイズを表します。 これは、Pythonリストの先頭にアイテムを追加すると、「線形時間」またはO(n)
(「BigO ofn」)で実行されることを意味します。
一般に、O(1)
プロシージャはO(n)
プロシージャよりも高速です。
Pythonリストの先頭に挿入できます。
favorite_fish_list = ["Sammy", "Jamie", "Mary"] # O(n) performance favorite_fish_list.insert(0, "Alice") print(favorite_fish_list)
次のように実行すると、次のような出力が表示されます。
Output['Alice', 'Sammy', 'Jamie', 'Mary']
リストの.insert(index, object)
メソッドを使用すると、favorite_fish_list
の先頭に"Alice"
を挿入できます。 ただし、リストの先頭に挿入すると、O(n)
のパフォーマンスが得られます。 favorite_fish_list
の長さが長くなると、リストの先頭に魚を挿入する時間は比例して長くなり、時間がかかります。
collections
モジュールのdeque
(「デッキ」と発音)はリストのようなオブジェクトであり、シーケンスの最初または最後に一定の時間(O(1)
でアイテムを挿入できます。 ]) パフォーマンス。
deque
の先頭にアイテムを挿入します。
from collections import deque favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"]) # O(1) performance favorite_fish_deque.appendleft("Alice") print(favorite_fish_deque)
このコードを実行すると、次の出力が表示されます。
Outputdeque(['Alice', 'Sammy', 'Jamie', 'Mary'])
既存の要素のコレクション(この場合は3つのお気に入りの魚の名前のリスト)を使用して、deque
をインスタンス化できます。 favorite_fish_deque
のappendleft
メソッドを呼び出すと、コレクションの先頭にO(1)
のパフォーマンスでアイテムを挿入できます。 O(1)
のパフォーマンスとは、favorite_fish_deque
に数千または数百万の要素がある場合でも、favorite_fish_deque
の先頭にアイテムを追加するのにかかる時間が長くならないことを意味します。
注: deque
は、リストよりも効率的にシーケンスの先頭にエントリを追加しますが、deque
は、リストよりも効率的にすべての操作を実行するわけではありません。 たとえば、deque
のランダムなアイテムにアクセスすると、O(n)
のパフォーマンスが得られますが、リストのランダムなアイテムにアクセスすると、O(1)
のパフォーマンスが得られます。 コレクションのいずれかの側から要素をすばやく挿入または削除することが重要な場合は、deque
を使用します。 時間パフォーマンスの完全な比較は、Pythonのwikiで入手できます。
結論
collections
モジュールは、Python標準ライブラリの強力な部分であり、データを簡潔かつ効率的に処理できます。 このチュートリアルでは、namedtuple
、defaultdict
、deque
を含むcollections
モジュールによって提供される3つのクラスについて説明しました。
ここから、コレクションモジュールのドキュメントを使用して、他の利用可能なクラスとユーティリティについて詳しく知ることができます。 Python全般の詳細については、Python3チュートリアルシリーズでコーディングする方法をご覧ください。