コンテナ

php では array で添字が数値の連続した配列も、そうじゃない連想配列も 格納していましたが、 Python ではこれらが区別されています。

list

list (リスト)型は配列で、 php でいうと添え字が 0 から連続した整数である ような array です。文字列と同じく、スライスが利用できます。

>>> [1,2,3]               # リテラル
[1, 2, 3]
>>> [0,1] * 3             # * で繰り返し
[0, 1, 0, 1, 0, 1]
>>> L = [0, 1, 2, 3, 4]
>>> L[:3]                 # スライス
[0, 1, 2]
>>> L[3:]
[3, 4]
>>> L[::-1]
[4, 3, 2, 1, 0]
>>> 2 in L                # 包含チェック
True

文字列は immutable だったのでオブジェクトの変更ができませんでしたが、 list は mutable で、いろいろな操作ができます。

>>> L.append(-1)               # 要素の追加
>>> L
[0, 1, 2, 3, 4, -1]
>>> L = L + [99, 100]          # list の連結
>>> L
[0, 1, 2, 3, 4, -1, 99, 100]
>>> L.reverse()                # 逆転
>>> L
[100, 99, -1, 4, 3, 2, 1, 0]
>>> del L[:2]                  # スライスで指定した範囲の要素削除
>>> L
[-1, 4, 3, 2, 1, 0]
>>> del L[::2]
>>> L
[4, 2, 0]
>>> L.insert(1, 1234)          # 挿入
>>> L
[4, 1234, 2, 0]
>>> L.pop(1)                   # 取り出して削除
1234
>>> L
[4, 2, 0]

immutable なオブジェクトでは += 演算子が新しいオブジェクトを作成することが保証されていましたが、 mutable なオブジェクトでは、 += 演算子で新しいオブジェクトを作成せずに自分の状態を更新する ことができます。 オブジェクトの状態を更新したくない場面では、そのオブジェクトが immutable だと分かっている場合を除いて、 a += b ではなく a = a + b を使う必要があります。

>>> L = [1,2,3]
>>> X=L
>>> L += [4]     # L 自体に [4] を連結する
>>> X is L       # X と L は同一なので
True
>>> X            # X の内容も同じように更新される
[1, 2, 3, 4]
>>> L = L + [5]  # L に [5] を連結した新しい list を L に代入
>>> X is L       # L に入ってるのは新しい list なので X と同一でなくなった
False
>>> X            # X の内容は更新されない
[1, 2, 3, 4]
>>> L
[1, 2, 3, 4, 5]

php の list(a, b, c) = $x と同じように、内容を変数にアンパックすることができます。

>>> L = [4, 2, 0]
>>> a, b, c = L
>>> a
4
>>> b
2
>>> c
0

list の要素を for で列挙すると、その中身が取り出せます。インデックスと中身を セットで扱いたい場合は、 enumerate 関数を使います。

>>> for i in L:
...     print(i)
...
4
2
0
>>> for i,a in enumerate(L):
...     print(i, a)
...
0 4
1 2
2 0

ソート

list.sort() で、リストの要素を昇順にソートします。 降順にソートしたい場合は、 list.sort(reverse=True) を使います。

>>> L = ['banana', 'apple', 'kiwi']
>>> L.sort()
>>> L
['apple', 'banana', 'kiwi']
>>> L.sort(reverse=True)
>>> L
['kiwi', 'banana', 'apple']

ソート順をカスタマイズする場合は、 php のように比較関数を渡す方法もありますが、 比較に使われる「キー」となる値を返す key 関数を指定する方法が推奨されます。

例えば、文字列の長さでソートする場合、php では次のように書きました。

<?php
function len_cmp($a, $b) {
    return strlen($a) - strlen($b);
}
usort($a, 'len_cmp');

Python で同じように比較関数を使ってソートする場合は、 -1/0/1 を返すのではなく、 左が小さい時に True を返す関数を作って、 sort() メソッドの cmp 引数に指定します。

def len_cmp(a, b):
    return len(a) < len(b)

a.sort(cmp=len_cmp)

一方、この場合の比較のキーは「文字列の長さ」なので、文字列を受け取って文字列の長さを返す関数、 つまり len が key 関数になります。

a.sort(key=len)

文字列の長さだけでなく、同じ長さなら普通にアルファベット順で比較するような key 関数を書いてみましょう。

a.sort(key=lambda x: (len(x), x))

この lambda は、下で解説する tuple を作って返してます。 tuple が比較されるときは 左から比較されるので、 (文字列の長さ, 文字列自体) の tuple を key にできるわけです。

range

for ループの所で出てきた range 関数は range オブジェクトを作成します。

>>> range(3)
range(0, 3)
>>> for i in _:
...     print(i)
0
1
2

range オブジェクトのようにループできるものを、 iterable (列挙可能) と呼びます。 iterable は、 list 関数で list にできます。

>>> list(range(3))
[0, 1, 2]

range 関数は start, end, step を指定することができます。 生成される値は、 step 間隔で、範囲は start 以上 end 未満になります。 (step が負なら start 以下で end より大きいです。)

>>> list(range(1,4))    # 引数2つなら step が省略されて 1 になる
[1, 2, 3]
>>> list(range(4))      # 引数1つなら start も省略されて 0 になる
[0, 1, 2, 3]
>>> list(range(1, 10, 3))
[1, 4, 7]
>>> range(10, 1, -3)
[10, 7, 4]

tuple

tuple は list とよく似ていますが、より軽量で、 immutable です。 リテラルは list に似ていますが少し複雑で、 []くくらずに , で区切ると tuple になります。しかし、空のリストではカンマで区切る値が無いので、 () と書きます。要素が1つの場合は、値の後ろにカンマをつけます。

>>> x = 1,2,3
>>> x
(1, 2, 3)
>>> ()    # 空の tuple
()
>>> 3,    # 要素が1つの tuple
(3,)
>>> (3)   # 括弧でくくるだけだと、ただの式のグループ化になってしまう.
3
>>> x = y = (1,2)
>>> x is y
True
>>> y
(1, 2)
>>> x += (5,)   # tuple は immutable なので、 += は新しい tuple を作る.
>>> x
(1, 2, 5)
>>> y
(1, 2)

tuple は後で説明するように dict のキーにできるという利点があります。 また、 list でアンパックができることは先に紹介しましたが、 tuple でも同じく アンパックが可能で、 tuple の方が軽量なので関数から複数の値を戻り値として 返したい時は普通 tuple を使います。 関数につていは後で扱いますが、一足先に複数の値を返す関数のサンプルを書いておきます。

>>> def foo():
...     return 1,2
...
>>> a,b = foo()
>>> a
1
>>> b
2

dict

dict は順序のない連想配列です。リテラルは JSON と同じ構文をしています。

>>> d = {'foo': 'bar', 'fizz': 'buzz'}
>>> d
{'foo': 'bar', 'fizz': 'buzz'}
>>> d['foo']
'bar'
>>> d['hoge']
Traceback (most recent call last):
  File "<ipython-input-59-a325c9d00d83>", line 1, in <module>
    d['hoge']
KeyError: 'hoge'

dict のキーには、数値や None や True, False のような immutable な値と、 immutable な値だけを要素に持つ tuple などが利用できます。 for 文や in 演算子を dict に対して直接実行した場合、キーがその 対象になります。

>>> d[(1,2)] = 3
>>> d
{(1, 2): 3, 'foo': 'bar', 'fizz': 'buzz'}
>>> 'foo' in d
True
>>> 'bar' in d
False
>>> for k in d:
...     print(k)
...
(1, 2)
foo
fizz

keys() というメソッドでキーの、 view を取得することができます。 view は、後述する set 型のような振る舞いをしますが、元の dict の内容を反映します。 values() で値の、 items()(key, value) の形の view を返します。

>>> d.keys()
dict_keys([(1, 2), 'foo', 'fizz'])
>>> d.values()
dict_values([3, 'bar', 'buzz'])
>>> d.items()
dict_items([((1, 2), 3), ('foo', 'bar'), ('fizz', 'buzz')])
>>> 'bar' in d.values()
True
>>> for k, v in d.items():
...     print(k, v)
...
(1, 2) 3
foo bar
fizz buzz
>>> d['X'] = 'Y'
>>> d.keys()
dict_keys([(1, 2), 'foo', 'fizz', 'X'])

http://www.python.jp/doc/release/library/stdtypes.html#dict-views も参照してください。

Note

Python 2 では、 keys(), values(), items() は view ではなく list を返していました。

一気にリスト全体を構築するとメモリの無駄なので、 Python 2.6 からは値を1つずつ取り出す itervalues(), iteritems() が追加され、 Python 2.7 では Python 3 と同じく view を返す viewvalues(), viewitems() が追加されました。

挿入順を保存する dict

dict は挿入順を保存しません。でも、 YAML や JSON や設定ファイルを扱う場合など、 順序を保存するような key-value 型が欲しいケースがあります。その場合は dict を拡張した OrderedDict を利用します。これは組み込み型ではなく標準ライブラリなので、 import 文を利用します。

>>> from collections import OrderedDict
>>> od = OrderedDict()
>>> od['foo'] = 1
>>> od['bar'] = 2
>>> od['baz'] = 3
>>> od
OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)])
>>> list(od.items())
[('foo', 1), ('bar', 2), ('baz', 3)]

set

set (集合)型は、順序がなく、重複を許さないコンテナ型です。 数学の集合をイメージするとすぐ理解できると思います。

>>> s = set(range(5))
>>> s
{0, 1, 2, 3, 4}
>>> s.add(3)
>>> s
{0, 1, 2, 3, 4}
>>> s.remove(3)
>>> s
{0, 1, 2, 4}
>>> s.add(5)
>>> s
{0, 1, 2, 4, 5}

和集合や差集合、共通部分集合などを計算することができます。

>>> t = set(range(0,8,2))
>>> t
{0, 2, 4, 6}
>>> s
{0, 1, 2, 4, 5}
>>> t & s         # 共通部分集合
{0, 2, 4}
>>> t | s         # 和集合
{0, 1, 2, 4, 5, 6}
>>> t - s         # 差集合
{6}
>>> s - t
{1, 5}

もちろん、 forin を利用することもできます。

>>> 3 in t
False
>>> for c in t:
...     print(c)
...
0
2
4
6

内包表記

リストを生成するコードを簡潔に書くために、リスト内包表記があります。 例えば次のようなコードは:

>>> L = []
>>> for i in range(10):
...     if i % 3 == 0:
...         L.append(i+1)
...
>>> L
[1, 4, 7, 10]

次のようにかけます。

>>> L = [i+1 for i in range(10) if i%3 == 0]

もとのコードと比べてみるとわかるように、

  1. 全体を [] で囲う
  2. 一番内側の .append(式) を、 [] の中の先頭に書く
  3. 続けて、 for と if をもとの順番で、 : は除いて書く

という構造になっています。 if は、 for の後ろに1つしか書けないことに気をつけてください。 上のルールに従えば、ネストしたループも内包表記でかけます。

>>> [(x,y)
...  for x in range(10) if x%3==0
...  for y in range(20) if y%7==0]
[(0, 0), (0, 7), (0, 14), (3, 0), (3, 7), (3, 14), (6, 0), (6, 7), (6, 14), (9, 0), (9, 7), (9, 14)]

ただし、ネストした内包表記は読みにくくなりやすいので、読みにくいと思ったら 普通のループで書いてしまいましょう。

リスト内包と似た、ジェネレータ内包表記もあります。 例えば次のような、渡された iterable を1つずつ表示する関数があるとします。

def print_all(iterable):
    for it in iterable:
        print(it)

これを呼び出すとき、 print_all([i*2 for i in L]) と書くと、一旦 L と同じ サイズのリストを作ってから関数を呼び出すので、 L と同じメモリを使ってしまいます。 print_all(i*2 for i in L) と書けば、リストではなくジェネレータで、 要素を1つずつ渡すことができます。 (TODO: 別の場所でジェネレータを解説する)

Python 2.7 と Python 3 では、リストとジェネレータ以外に、 dict と set も 内包表記できます。 どちらも Python 2.6 までは、コンストラクタにリスト内包やジェネレータ内包で 書いた iterable を渡すことで実現していました。

>>> {i:i*2 for i in range(10)}  # dict 内包
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}
>>> dict((i, i*2) for i in range(10))  # Python 2.6 までの書き方
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

>>> {i*2 for i in range(10)}    # set 内包
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
>>> set(i*2 for i in range(10)) # Python 2.6 までの書き方
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}