5. データ構造—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.8/tutorial/datastructures
移動先:案内検索

5.5。 データ構造

この章では、すでに学んだいくつかのことをより詳細に説明し、いくつかの新しいことも追加します。

5.1。 リストの詳細

リストデータ型には、さらにいくつかのメソッドがあります。 リストオブジェクトのすべてのメソッドは次のとおりです。

list.append(x)
リストの最後にアイテムを追加します。 a[len(a):] = [x]と同等です。
list.extend(iterable)
iterableのすべての項目を追加して、リストを拡張します。 a[len(a):] = iterableと同等です。
list.insert(i, x)
指定された位置にアイテムを挿入します。 最初の引数は挿入する前の要素のインデックスであるため、a.insert(0, x)はリストの先頭に挿入され、a.insert(len(a), x)a.append(x)と同等です。
list.remove(x)
値が x に等しい最初のアイテムをリストから削除します。 そのようなアイテムがない場合、 ValueError が発生します。
list.pop([i])
リスト内の指定された位置にあるアイテムを削除して、返却します。 インデックスが指定されていない場合、a.pop()はリストの最後のアイテムを削除して返します。 (メソッドシグネチャの i を囲む角かっこは、パラメータがオプションであることを示しており、その位置に角かっこを入力する必要はありません。 この表記は、Pythonライブラリリファレンスで頻繁に見られます。)
list.clear()
リストからすべてのアイテムを削除します。 del a[:]と同等です。
list.index(x[, start[, end]])

値が x に等しい最初のアイテムのリストでゼロベースのインデックスを返します。 そのようなアイテムがない場合、 ValueError を発生させます。

オプションの引数 start および end は、スライス表記の場合と同様に解釈され、検索をリストの特定のサブシーケンスに制限するために使用されます。 返されるインデックスは、 start 引数ではなく、完全なシーケンスの先頭を基準にして計算されます。

list.count(x)
x がリストに表示された回数を返します。
list.sort(*, key=None, reverse=False)
リストの項目をその場でソートします(引数はソートのカスタマイズに使用できます。説明については、 sorted()を参照してください)。
list.reverse()
リストの要素を元の場所に戻します。
list.copy()
リストの浅いコピーを返します。 a[:]と同等です。

ほとんどのリストメソッドを使用する例:

>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4)  # Find next banana starting a position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'

リストを変更するだけのinsertremovesortのようなメソッドには、戻り値が出力されないことに気付いたかもしれません。デフォルトのNoneを返します。 1 これは、Pythonのすべての可変データ構造の設計原則です。

もう1つ気付くかもしれないのは、すべてのデータを並べ替えたり比較したりできるわけではないということです。 たとえば、[None, 'hello', 10]は、整数を文字列と比較できず、 None を他のタイプと比較できないため、並べ替えられません。 また、順序関係が定義されていないタイプもあります。 たとえば、3+4j < 5+7jは有効な比較ではありません。

5.1.1。 リストをスタックとして使用する

listメソッドを使用すると、リストをスタックとして非常に簡単に使用できます。ここで、最後に追加された要素は、最初に取得された要素(「後入れ先出し」)です。 スタックの一番上にアイテムを追加するには、append()を使用します。 スタックの最上位からアイテムを取得するには、明示的なインデックスなしでpop()を使用します。 例えば:

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

5.1.2。 リストをキューとして使用する

リストをキューとして使用することもできます。ここで、追加された最初の要素は、取得された最初の要素です(「先入れ先出し」)。 ただし、リストはこの目的には効率的ではありません。 リストの最後からの追加とポップは高速ですが、リストの最初からの挿入またはポップの実行は低速です(他のすべての要素を1つシフトする必要があるため)。

キューを実装するには、両端からの高速な追加とポップを行うように設計された collections.deque を使用します。 例えば:

>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry")           # Terry arrives
>>> queue.append("Graham")          # Graham arrives
>>> queue.popleft()                 # The first to arrive now leaves
'Eric'
>>> queue.popleft()                 # The second to arrive now leaves
'John'
>>> queue                           # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])

5.1.3。 リスト内包表記

リスト内包表記は、リストを作成するための簡潔な方法を提供します。 一般的なアプリケーションは、各要素が別のシーケンスまたは反復可能の各メンバーに適用された操作の結果である新しいリストを作成するか、特定の条件を満たす要素のサブシーケンスを作成することです。

たとえば、次のような正方形のリストを作成するとします。

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

これにより、xという名前の変数が作成(または上書き)され、ループの完了後も存在することに注意してください。 以下を使用して、副作用のない正方形のリストを計算できます。

squares = list(map(lambda x: x**2, range(10)))

または、同等に:

squares = [x**2 for x in range(10)]

これはより簡潔で読みやすいです。

リスト内包表記は、式とそれに続くfor句、および0個以上のforまたはif句を含む角かっこで構成されます。 結果は、それに続くforおよびif句のコンテキストで式を評価した結果の新しいリストになります。 たとえば、このlistcompは、2つのリストの要素が等しくない場合、それらを組み合わせます。

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

そしてそれは同等です:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

for ステートメントと if ステートメントの順序がこれらのスニペットの両方でどのように同じであるかに注意してください。

式がタプルの場合(例: 前の例の(x, y))、括弧で囲む必要があります。

>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1, in <module>
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

リスト内包表記には、複雑な式や入れ子関数を含めることができます。

>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']

5.1.4。 ネストされたリスト内包

リスト内包表記の最初の式は、別のリスト内包表記を含む任意の式にすることができます。

長さ4の3つのリストのリストとして実装された3x4行列の次の例を考えてみます。

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]

次のリスト内包表記は、行と列を転置します。

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

前のセクションで見たように、ネストされたlistcompは、それに続く for のコンテキストで評価されるため、この例は次のようになります。

>>> transposed = []
>>> for i in range(4):
...     transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

これは、次と同じです。

>>> transposed = []
>>> for i in range(4):
...     # the following 3 lines implement the nested listcomp
...     transposed_row = []
...     for row in matrix:
...         transposed_row.append(row[i])
...     transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

現実の世界では、複雑なフローステートメントよりも組み込み関数を優先する必要があります。 zip()関数は、このユースケースに最適です。

>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

この行のアスタリスクの詳細については、引数リストの解凍を参照してください。


5.2。 NSdel 声明

値の代わりにインデックスを指定してリストからアイテムを削除する方法があります: del ステートメント。 これは、値を返すpop()メソッドとは異なります。 delステートメントを使用して、リストからスライスを削除したり、リスト全体をクリアしたりすることもできます(これは、以前にスライスに空のリストを割り当てることで行いました)。 例えば:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]

del を使用して、変数全体を削除することもできます。

>>> del a

以降、aという名前を参照すると、エラーになります(少なくとも別の値が割り当てられるまで)。 del の他の用途については後で説明します。


5.3。 タプルとシーケンス

リストと文字列には、インデックス作成やスライス操作など、多くの一般的なプロパティがあることがわかりました。 これらは、シーケンスデータ型の2つの例です(シーケンスタイプ—リスト、タプル、範囲を参照)。 Pythonは進化する言語であるため、他のシーケンスデータ型が追加される場合があります。 別の標準シーケンスデータ型もあります:タプル

タプルは、コンマで区切られたいくつかの値で構成されます。次に例を示します。

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
... t[0] = 88888
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])

ご覧のとおり、出力時のタプルは常に括弧で囲まれているため、ネストされたタプルは正しく解釈されます。 とにかく括弧が必要になることがよくありますが、それらは周囲の括弧の有無にかかわらず入力できます(タプルがより大きな式の一部である場合)。 タプルの個々のアイテムに割り当てることはできませんが、リストなどの可変オブジェクトを含むタプルを作成することはできます。

タプルはリストに似ているように見えるかもしれませんが、さまざまな状況でさまざまな目的で使用されることがよくあります。 タプルは不変であり、通常、アンパック(このセクションの後半を参照)またはインデックス付け( namedtuples の場合は属性によっても)を介してアクセスされる要素の異種シーケンスが含まれます。 リストは可変であり、それらの要素は通常同種であり、リストを反復処理することでアクセスされます。

特別な問題は、0個または1個の項目を含むタプルの構築です。構文には、これらに対応するためのいくつかの追加の癖があります。 空のタプルは、空の括弧のペアで構成されます。 1つの項目を持つタプルは、値の後にコンマを付けて作成されます(1つの値を括弧で囲むだけでは不十分です)。 醜いですが、効果的です。 例えば:

>>> empty = ()
>>> singleton = 'hello',    # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

ステートメントt = 12345, 54321, 'hello!'は、タプルパッキングの例です。値1234554321、および'hello!'はタプルに一緒にパックされます。 逆の操作も可能です。

>>> x, y, z = t

これは、適切にはシーケンスアンパックと呼ばれ、右側の任意のシーケンスで機能します。 シーケンスのアンパックでは、等号の左側に、シーケンス内の要素と同じ数の変数が必要です。 複数の割り当ては、実際にはタプルパッキングとシーケンスアンパックの単なる組み合わせであることに注意してください。


5.4。 セット

Pythonには、セットのデータ型も含まれています。 セットは、重複する要素のない順序付けられていないコレクションです。 基本的な用途には、メンバーシップのテストと重複エントリの排除が含まれます。 セットオブジェクトは、和集合、共通部分、差、対称差などの数学演算もサポートします。

中括弧または set()関数を使用してセットを作成できます。 注:空のセットを作成するには、{}ではなくset()を使用する必要があります。 後者は空の辞書を作成します。これは次のセクションで説明するデータ構造です。

簡単なデモンストレーションは次のとおりです。

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # fast membership testing
True
>>> 'crabgrass' in basket
False

>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b                              # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # letters in both a and b
{'a', 'c'}
>>> a ^ b                              # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}

リスト内包表記と同様に、集合の内包表記もサポートされています。

>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

5.5。 辞書

Pythonに組み込まれているもう1つの便利なデータ型は、辞書です(マッピングタイプ— dict を参照)。 辞書は、他の言語では「連想記憶」または「連想配列」として見つかることがあります。 ある範囲の数値で索引付けされるシーケンスとは異なり、辞書はキーで索引付けされます。これは、任意の不変タイプにすることができます。 文字列と数字は常にキーにすることができます。 タプルに文字列、数字、またはタプルのみが含まれている場合は、タプルをキーとして使用できます。 タプルに直接または間接的に変更可能なオブジェクトが含まれている場合、それをキーとして使用することはできません。 リストは、インデックス割り当て、スライス割り当て、またはappend()extend()などのメソッドを使用してその場で変更できるため、リストをキーとして使用することはできません。

ディクショナリは、 key:value ペアのセットと考えるのが最善ですが、キーは(1つのディクショナリ内で)一意である必要があります。 中括弧のペアは空の辞書を作成します:{}。 中括弧内にkey:valueペアのコンマ区切りリストを配置すると、最初のkey:valueペアがディクショナリに追加されます。 これは、辞書が出力に書き込まれる方法でもあります。

ディクショナリの主な操作は、キーを使用して値を格納し、キーを指定して値を抽出することです。 delでキーと値のペアを削除することもできます。 すでに使用されているキーを使用して保存すると、そのキーに関連付けられている古い値は忘れられます。 存在しないキーを使用して値を抽出するとエラーになります。

辞書でlist(d)を実行すると、辞書で使用されているすべてのキーのリストが挿入順に返されます(並べ替える場合は、代わりにsorted(d)を使用してください)。 単一のキーが辞書にあるかどうかを確認するには、 in キーワードを使用します。

辞書を使用した小さな例を次に示します。

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

dict()コンストラクターは、キーと値のペアのシーケンスから直接辞書を作成します。

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

さらに、dict内包表記を使用して、任意のキーおよび値式から辞書を作成できます。

>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

キーが単純な文字列の場合、キーワード引数を使用してペアを指定する方が簡単な場合があります。

>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

5.6。 ループテクニック

辞書をループする場合、items()メソッドを使用して、キーと対応する値を同時に取得できます。

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

シーケンスをループする場合、 enumerate()関数を使用して、位置インデックスと対応する値を同時に取得できます。

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

2つ以上のシーケンスを同時にループするには、エントリを zip()関数とペアにすることができます。

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

シーケンスを逆方向にループするには、最初に順方向のシーケンスを指定してから、 reverse()関数を呼び出します。

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

ソートされた順序でシーケンスをループするには、 sorted()関数を使用します。この関数は、ソースを変更せずに、新しいソートされたリストを返します。

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

リストをループしているときに、リストを変更したくなることがあります。 ただし、多くの場合、代わりに新しいリストを作成する方が簡単で安全です。

>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
...     if not math.isnan(value):
...         filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]

5.7。 条件の詳細

whileおよびifステートメントで使用される条件には、比較だけでなく、任意の演算子を含めることができます。

比較演算子inおよびnot inは、値がシーケンスで発生する(発生しない)かどうかをチェックします。 演算子isis notは、2つのオブジェクトが本当に同じオブジェクトであるかどうかを比較します。 これは、リストなどの可変オブジェクトにのみ関係します。 すべての比較演算子の優先度は同じであり、すべての数値演算子の優先度よりも低くなっています。

比較は連鎖させることができます。 たとえば、a < b == cは、ab未満であり、さらにbcと等しいかどうかをテストします。

比較は、ブール演算子andおよびorを使用して組み合わせることができ、比較(または他のブール式)の結果はnotで否定できます。 これらは、比較演算子よりも優先度が低くなります。 それらの間では、notの優先度が最も高く、orの優先度が最も低いため、A and not B or C(A and (not B)) or Cと同等です。 いつものように、括弧を使用して目的の構成を表すことができます。

ブール演算子andおよびorは、いわゆる短絡演算子です。これらの引数は左から右に評価され、結果が決定されるとすぐに評価が停止します。 。 たとえば、ACが真であるが、Bが偽である場合、A and B and Cは式Cを評価しません。 ブール値としてではなく一般値として使用される場合、短絡演算子の戻り値は最後に評価される引数です。

比較または他のブール式の結果を変数に割り当てることができます。 例えば、

>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

Pythonでは、Cとは異なり、式内の代入は walrus演算子 :=を使用して明示的に行う必要があることに注意してください。 これにより、Cプログラムで発生する一般的なクラスの問題が回避されます。==が意図されているときに式に=と入力します。


5.8。 シーケンスと他のタイプの比較

シーケンスオブジェクトは通常、同じシーケンスタイプの他のオブジェクトと比較できます。 比較では、辞書式の順序を使用します。最初に最初の2つの項目が比較され、それらが異なる場合は、これによって比較の結果が決まります。 それらが等しい場合は、次の2つの項目が比較され、いずれかのシーケンスが使い果たされるまで続きます。 比較する2つの項目自体が同じタイプのシーケンスである場合、辞書式比較は再帰的に実行されます。 2つのシーケンスのすべての項目が等しいと比較される場合、シーケンスは等しいと見なされます。 一方のシーケンスがもう一方の最初のサブシーケンスである場合、短いシーケンスは小さい(小さい)シーケンスです。 文字列の辞書式順序は、Unicodeコードポイント番号を使用して個々の文字を順序付けます。 同じタイプのシーケンス間の比較のいくつかの例:

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

異なるタイプのオブジェクトを<または>と比較することは、オブジェクトに適切な比較方法があれば合法であることに注意してください。 たとえば、混合数値型は数値に従って比較されるため、0は0.0になります。 それ以外の場合、インタプリタは任意の順序を提供するのではなく、 TypeError 例外を発生させます。

脚注

1
d->insert("a")->remove("b")->sort();など、他の言語は変更されたオブジェクトを返す場合があります。これにより、メソッドの連鎖が可能になります。