著者は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)

このコードを実行すると、次の出力が表示されます。

Output
Fish(name='Sammy', species='shark', tank='tank-a')

sammyは、Fishクラスを使用してインスタンス化されます。 sammyは、明確に名前が付けられた3つの要素を持つタプルです。

sammyのフィールドには、名前または従来のタプルインデックスを使用してアクセスできます。

print(sammy.species)
print(sammy[1])

これらの2つのprint呼び出しを実行すると、次の出力が表示されます。

Output
shark 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は、辞書内の情報をすばやく簡潔に組み立てるのに役立ちます。

defaultdictKeyErrorを発生させません。 キーが存在しない場合、defaultdictは代わりにプレースホルダー値を挿入して返します:

from collections import defaultdict

my_defaultdict = defaultdict(list)

print(my_defaultdict["missing"])

このコードを実行すると、次のような出力が表示されます。

Output
[]

defaultdictは、KeyErrorをスローする代わりに、プレースホルダー値を挿入して返します。 この場合、プレースホルダー値をリストとして指定しました。

対照的に、通常の辞書は、欠落しているキーにKeyErrorをスローします。

my_regular_dict = {}

my_regular_dict["missing"]

このコードを実行すると、次のような出力が表示されます。

Output
Traceback (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)

このコードを実行すると、次の出力が表示されます。

Output
defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

fish_names_by_tankdefaultdictとして宣言され、デフォルトでは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の存在をチェックする必要があることを意味します。 。 tankfish_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)

このコードを実行すると、次の出力が表示されます。

Output
deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

既存の要素のコレクション(この場合は3つのお気に入りの魚の名前のリスト)を使用して、dequeをインスタンス化できます。 favorite_fish_dequeappendleftメソッドを呼び出すと、コレクションの先頭にO(1)のパフォーマンスでアイテムを挿入できます。 O(1)のパフォーマンスとは、favorite_fish_dequeに数千または数百万の要素がある場合でも、favorite_fish_dequeの先頭にアイテムを追加するのにかかる時間が長くならないことを意味します。

注: dequeはリストよりも効率的にシーケンスの先頭にエントリを追加しますが、dequeはリストよりも効率的にすべての操作を実行するわけではありません。 たとえば、dequeのランダムなアイテムにアクセスすると、O(n)のパフォーマンスが得られますが、リストのランダムなアイテムにアクセスすると、O(1)のパフォーマンスが得られます。 コレクションのいずれかの側から要素をすばやく挿入または削除することが重要な場合は、dequeを使用します。 時間パフォーマンスの完全な比較は、Pythonのwikiで入手できます

結論

collectionsモジュールは、Python標準ライブラリの強力な部分であり、データを簡潔かつ効率的に処理できます。 このチュートリアルでは、namedtupledefaultdictdequeを含むcollectionsモジュールによって提供される3つのクラスについて説明しました。

ここから、コレクションモジュールのドキュメントを使用して、他の利用可能なクラスとユーティリティについて詳しく知ることができます。 Python全般の詳細については、Python3チュートリアルシリーズでコーディングする方法をご覧ください。