namedTuple

各要素に名前がついている tuple を作成できます。
C言語の構造体のように、いくつかの変数を1つのオブジェクトでまとめる目的で使うことができます。

C言語の構造体

struct Rect
{
    int x;
    int y;
    int width;
    int height;
}

普通の tuple も同じ目的で使えますが、各要素に添字でアクセスすることになるため、プログラムの可読性が落ちるという欠点があります。

以下は tuple で2次元空間上の2点を tuple で表し、ユークリッド距離を計算する例です。

>>> import math
>>> pt1 = (1.0, 4.0)
>>> pt2 = (7.0, 6.0)
>>> math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
6.324555320336759

namedTuple を使うと以下のようになります。
こちらのほうが tuple の各要素に名前でアクセスできるため、コードがよりわかりやすくなったのではないでしょうか。

Point = namedtuple("Point", "x y")
>>> from collections import namedtuple
>>> import math
>>> Point = namedtuple("TEST", "x, y")
>>> pt1 = Point(x=1.0, y=4.0)
>>> pt2 = Point(x=7.0, y=6.0)
>>> math.sqrt((pt1.x - pt2.x) ** 2 + (pt1.y - pt2.y) ** 2)
6.324555320336759

namedTuple の作成

まず namedtuple(typename, field_names) でクラスの定義を作成します。
typename には tuple の名前を指定します。
field_names には tuple が持つ要素をスペース区切りの文字列、カンマ区切りの文字列、または文字列のリストで指定します。
以下はいずれも同じ namedtuple が作成できます。

# スペース区切りの文字
>>> Rect = namedtuple("Rect", "x y width height")
# カンマ区切りの文字列
>>> Rect = namedtuple("Rect", "x, y, width, height")
# 文字列のリスト
>>> Rect = namedtuple("Rect", ["x", "y", "width", "height"])

フィールド名には def、for などの予約語とアンダーバーで始まる名前以外ならなんでも構いません。

# 予約後はNG
>>> Rect = namedtuple("Rect", "for def")
ValueError: Type names and field names cannot be a keyword: 'for'
# アンダーバーで始まる名前もNG
>>> Rect = namedtuple("Rect", "_hoge")
alueError: Field names cannot start with an underscore: '_hoge'

インスタンスの生成

Rect(x, y, width, height) という関数を呼び出して作成します。
以下、Python の引数の渡し方3通り。

>>> Rect = namedtuple("Rect", "x y width height")
# 名前付き引数で初期化
>>> rect = Rect(x=0, y=0, width=100, height=200)
# 通常の渡し方で初期化
>>> rect = Rect(0, 0, 100, 200)
# ** 演算子
>>> args = {"x": 0, "y": 0, "width": 100, "height": 200}
>>> rect = Rect(**args)

_make(iterable) を使うと、リストなど iterable なオブジェクトで初期化できます。
namedTuple のフィールドの数と iterable の要素数は一致している必要があります。

# _make() で初期化
>>> rect = Rect._make([0, 0, 100, 200])

namedTuple の参照

通常の tuple と同じ使い方ができます。

>>> Rect = namedtuple("Rect", "x y width height")
>>> rect = Rect(x=0, y=0, width=100, height=200)
# 通常の tuple と同じように展開できる。
>>> x, y, width, height = rect
# 通常の tuple と同じように添字アクセスもできる。
>>> rect[3]
200

フィールド名によるアクセスもできます。

>>> Rect = namedtuple("Rect", "x y width height")
>>> rect = Rect(x=0, y=0, width=100, height=200)
>>> rect.width
100

作成済みの namedtuple のフィールドは変更できません。
代わりに指定したフィールドを新しい値で置き換えた namedtuple を返す _replace(kwargs) を使います。

>>> Rect = namedtuple("Rect", "x y width height")
>>> rect = Rect(x=0, y=0, width=100, height=200)
>>> rect
Rect(x=0, y=0, width=100, height=200)
# 代入はNG
>>> rect.x = 100
AttributeError: can't set attribute
# _replace() を使う
>>> rect = rect._replace(x=20, y=20)
>>> rect
Rect(x=20, y=20, width=100, height=200)

フィールド一覧の取得

>>> Rect = namedtuple("Rect", "x y width height")
>>> rect = Rect(x=0, y=0, width=100, height=200)
>>> rect._fields
('x', 'y', 'width', 'height')

Deque (double ended qeque)

日本語では両端キューと訳され、先頭または末尾に対して追加(PUSH)、取り出し(POP)が行えるデータ構造を Deque といいます。

Python で使えるデータ構造

Python の標準ライブラリで提供されている deque クラスの使い方を紹介します。

左側に要素を追加する

appendleft(x) で左側に要素を追加できます。

>>> from collections import deque
>>> data = deque([5, 17, 22, 45])
>>> data
deque([5, 17, 22, 45])
>>> data.appendleft(12)
deque([12, 5, 17, 22, 45])

左側に複数の要素を追加する

extendleft(iterable) で左側に複数の要素を追加できます。
iterable の先頭から順番に追加されていくため、順序が逆になることに注意してください。

>>> from collections import deque
>>> data = deque([5, 17, 22, 45])
>>> data
deque([5, 17, 22, 45])
>>> data.extendleft([1, 2, 3])
>>> data
deque([3, 2, 1, 5, 17, 22, 45])

左側から要素を取り出す

popleft() で左側から要素を取り出し、返り値として返します。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 45])
>>> data
deque([12, 5, 17, 22, 45])
>>> value = data.popleft()
>>> value
deque([5, 17, 22, 45])

左側の要素を確認する

インデックスは左側から 0, 1, … を指すようになっています。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 45])
>>> data[0]
12
>>> data[1]
5

右側に要素を追加する

append(x) で右側に要素を追加できます。

>>> from collections import deque
>>> data = deque([5, 17, 22, 45])
>>> data
deque([5, 17, 22, 45])
>>> data.append(12)
>>> data
deque([5, 17, 22, 45, 12])

右側に複数の要素を追加する

extend(iterable) で右側に複数の要素を追加できます。

>>> from collections import deque
>>> data = deque([5, 17, 22, 45])
>>> data
deque([5, 17, 22, 45])
>>> data.extend([1, 2, 3])
>>> data
deque([5, 17, 22, 45, 1, 2, 3])

右側から要素を取り出す

pop(x) で右側から要素を取り出し、返り値として返します。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 45])
>>> data
deque([12, 5, 17, 22, 45])
>>> value = data.pop()
>>> value
45
>>> data
deque([12, 5, 17, 22])

末尾の要素を確認する

インデックスは左側から 0, 1, … を指すようになっています。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 45])
>>> data[-1]
45
>>> data[-2]
22

初期化する。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 12])
>>> data
deque([12, 5, 17, 22, 12])
>>> data.clear()
>>> data
deque([])

ある値が存在する位置を調べる。

index(x[, start[, stop]]) で値 x が存在するインデックスを返します。
start, stop を指定すると start <= index < stop の範囲で検索します。 存在しない場合は、ValueError が発生します。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 12])
>>> data.index(12, 1, 5)
4
>>> data.index(40)
ValueError: 40 is not in deque

値を挿入する。

insert(i, x) で値 x をインデックス i の前に挿入できます。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 45])
>>> data.insert(1, 10)
>>> data
deque([12, 10, 5, 17, 22, 45])

値を削除する。

remove(value) で最初に見つかった値 value を削除します。
見つからない場合は、ValueError が発生します。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 12])
>>> data.remove(12)
>>> data
deque([5, 17, 22, 45])

等しい要素の数を数える。

count(value) で value と等しい要素を数えます。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 12])
>>> data.count(12)
2

要素数を調べる。

len() で要素数を取得できます。

>>> from collections import deque
>>> data = deque([12, 5, 17, 22, 12])
>>> len(data)
5

並びを反転させる。

reverse() で並びを反転させます。

>>> data = deque([12, 5, 17, 22, 45])
>>> data
deque([12, 5, 17, 22, 45])
>>> data.reverse()
>>> data
deque([45, 22, 17, 5, 12])

元の deque は変更せず、それを反転させた新たな deque を得たい場合は reversed() を使います。
返すのは逆順にアクセスしていく iterater なので、deque(iterable) を使い逆順の deque を作成します。

>>> data = deque([12, 5, 17, 22, 45])
>>> rev_iter = reversed(data)
>>> rev_iter
<_collections._deque_reverse_iterator object at 0x000001E2F8E50EF8>
>>> deque(rev_iter)
deque([45, 22, 17, 5, 12])

deque の長さを制限する。

deque([iterable[, maxlen]]) で maxlen を指定した場合、deque の長さが制限されます。
append()、leftappend()、extend()、leftextend() で要素を追加して maxlen より要素数が多くなった場合は追加した側と反対側の端にある要素が捨てられます。
insert(i, x) では、追加してあふれる場合は IndexError が発生して追加できません。

>>> data = deque([5, 17, 22, 45], 4)
>>> data.appendleft(12)
>>> data
# 左に追加して溢れたので、右端の 45 が捨てられた。
deque([12, 5, 17, 22], maxlen=4)