S-JIS[2018-01-07/2018-01-15] 変更履歴

Pythonのリスト

Python3.6.4のリストのメモ。


概要

Pythonのlistクラスはミュータブル(可変)なシーケンス。(JavaのList相当)

他言語での配列のような「a[n]」はシーケンス共通のアクセス方法であり、配列ではない。
初期化方法「[1, 2, 3]」はリストであり、配列ではない。
(Pythonの配列はarrayというクラスがある 。また、多次元配列を扱うNumPyもよく使われる)


リストのリテラル

リストのリテラル(リストの初期化)は値をカンマ区切りで並べて角括弧で囲む。

[値, …]
  説明
要素数0 a = [] 空リスト。
要素数1 a = [1]  
要素数3 a = [1, 2, 3]  
末尾カンマ a = [1,]
a = [1, 2, 3,]
末尾の値の後ろに(余分な)カンマを付けてもよい。このカンマは無視される。
アンパック a = [2, 3]
b = [1, *a, 4] # [1, 2, 3, 4]
関数呼び出し時の引数リストのアンパックと同様に、
リストのリテラルでも他のシーケンスの前に「*」を付けることでアンパックできる。[2018-01-13]
a = [1, *[2, 3], 4] # [1, 2, 3, 4]
2次元配列 aa = [[1, 2], [3, 4]]  

リスト内包表記


要素の取得・設定

リストの各要素の値を取得したり設定したりするには、(C言語やJavaの配列と同様に)角括弧で添字を指定する。
(リストだけでなくシーケンス共通)

添字は0始まりの整数。

a = [1, 2, 3]
v = a[0] # v=1

a[0] = 99
print(a) # →[99, 2, 3]
# 2次元配列の場合
aa = [[1, 2], [3, 4]]
v = aa[0][0] # v=1

aa[0][1] = 99
print(aa) # →[[1, 99], [3, 4]]

※listの2次元配列では「aa[0, 0]」のように書くことは出来ない。(NumPyのndarrayでは書ける)


添字には負の数を指定してもよい。
(大抵のプログラミング言語ではエラーになる。Pythonでも大丈夫かどうかはクラス次第だが、リストや文字列は大丈夫)

負の数を指定した場合、末尾から数えた番号になる。
すなわち、-1は一番末尾、-2は末尾から2番目。

a = ["x", "y", "z"]
print(a[-1]) # →z
print(a[-2]) # →y
print(a[-3]) # →x

指定した添字がリストの範囲外の場合は例外(IndexError)が発生する。

a = [1, 2, 3]
print(a[3])  # IndexError
print(a[-4]) # IndexError

アンパック代入

Scalaでパターンマッチと呼ぶ方法”で要素を取得することも出来る。
アンパック代入と呼ばれているらしい。[2018-01-13]

a = [1, 2, 3]
[x, y, z] = a # x=1, y=2, z=3

この場合、リストのサイズ(要素数)と代入先の変数の個数は同じでなければならない。(異なる場合はValueErrorが発生する)

代入元はリストである必要は無く、たぶんイテレート可能オブジェクト(主にシーケンス)であれば何でもよい。
代入先もリストではなくタプル形式(丸括弧で囲む)が使える。

t = (1, 2, 3)
[x, y, z] = t # x=1, y=2, z=3

[x, y, z] = range(3) # x=0, y=1, z=2

a = [1, 2, 3]
(x, y, z) = a # x=1, y=2, z=3

x, y, z = a   # x=1, y=2, z=3

代入先をアンダースコア「_」にすると、そこには値が代入されない(無視される)。
(この場合でも、要素数が一致していなければエラーになる)

a = [1, 2, 3]
[_, y, _] = a # y=2

_, y, _ = a   # y=2

変数の前に「*」を付けると、その位置に該当する全要素がリストになって入ってくる。
この場合は代入元の要素数が多くても良い。

a = [1, 2, 3, 4]
[x, y, *z] = a # x=1, y=2, z=[3, 4]
a = [1, 2]
[x, y, *z] = a # x=1, y=2, z=[]
a = [1, 2, 3, 4]
[_, y, *_] = a # y=2

「*」はどの位置の変数に付いていてもよいが、2つ以上は指定できない。

a = [1, 2, 3, 4]
[x, *y, z] = a # x=1, y=[2, 3], z=4
a = [1, 2, 3, 4]
[*x, y, z] = a # x=[1, 2], y=3, z=4

スライス記法

リストではスライス記法という書き方によってリストの一部分を取得・変更することが出来る。
(リストだけでなくシーケンス共通)

角括弧内の添字としてコロン「:」区切りで開始位置・終了位置を指定する。
位置は0始まりのインデックス。
終了位置はその位置自身の値を含まない。

a = ["a", "b", "c", "d", "e"]
s = a[1:3] # s=['b', 'c']

※listでは「a[1:2, 3:4]」のようにスライスを複数書くことは出来ない。(Pythonの文法上は許されており、NumPyのndarrayでは書ける)


要素を1個取得する場合の添字と同様に、負の数も指定できる。(末尾から数えた位置になる)

a = ["a", "b", "c", "d", "e"]
s = a[-3:-1] # s=['c', 'd']

要素を1個取得する場合の添字と異なり、範囲外の位置を指定してもエラーにならない。
開始位置が範囲外の場合は空リストになり、終了位置が範囲外の場合は開始位置以降の全要素が返る。
開始位置と終了位置が逆転している場合も空リストが返る。

a = ["a", "b", "c", "d", "e"]
s = a[2:1] # s=[]
s = a[6:6] # s=[]
s = a[3:9] # s=['d', 'e']

開始位置や終了位置は省略できる。
開始位置を省略した場合は0(先頭)、終了位置を省略した場合は末尾まで全てになる。

a = ["a", "b", "c", "d", "e"]
s = a[:3] # s=['a', 'b', 'c']
s = a[3:] # s=['d', 'e']

開始位置と終了位置を省略した場合は全要素、すなわちリスト全体のコピーになる。

a = ["a", "b", "c", "d", "e"]
c = a[:]
print(c == a) # True(内容は同一)
print(c is a) # False(インスタンスは異なる)

b = a # 単なる代入だと、参照をコピーしただけ
print(b == a) # True(内容は同一)
print(b is a) # True(インスタンスも同じ)

第3引数で、何個ずつ移動するかを指定できる。省略した場合は1。

a = ["a", "b", "c", "d", "e"]
s = a[1:4:2] # s=['b', 'd']
s = a[1:4:1] # s=['b', 'c', 'd']

リスト(ミュータブルなシーケンス)では、スライス記法によってリストの一部分を更新することが出来る。

a = ["a", "b", "c", "d", "e"]
a[1:4] = ["x", "y"] # a=['a', 'x', 'y', 'e']
# 代入元はシーケンスでなくても良い
a = ["a", "b", "c", "d", "e"]
a[1:4] = "z" # a=['a', 'z', 'e']
# 開始位置と終了位置を同じにすれば、挿入になる
a = ["a", "b", "c", "d", "e"]
a[1:1] = ["x", "y"] # a=['a', 'x', 'y', 'b', 'c', 'd', 'e']
# 空リストを代入すれば要素の削除になる
a = ["a", "b", "c", "d", "e"]
a[1:4] = [] # a=['a', 'e']

削除に関しては、del文を使うことも出来る。

a = ["a", "b", "c", "d", "e"]
del a[1:4] # a=['a', 'e']

スライス記法を実装する例


リストの操作

リストに関するメソッド・組み込み関数。

内容 例の結果 説明 Scala相当
要素数 len(["a", "b", "c"]) 3 リストの長さ(要素数)を取得する。 Seq("a", "b", "c").size
空判定 bool(["a"])
bool([])
True
False
Pythonでは空リストは偽、それ以外のリストは真。 Seq("a").nonEmpty
Seq().nonEmpty
s = ["a"]
if s: print("exist")
else: print("empty")
exist  
not ["a"]
not []
False
True
Seq("a").isEmpty
Seq().isEmpty
比較 list1 == list2
list1 != list2
list1 < list2
list1 > list2
list1 <= list2
list1 >= list2
    list1 == list2
list1 != list2
部分一致 "b" in ["a", "b", "c"] True 指定された値が含まれているかどうか。 Seq("a", "b", "c").contains("b")
探索 ["a", "b", "c", "b", "d"].count("b") 2 指定された値の個数を返す。 Seq("a", "b", "c", "b", "d").count(_ == "b")
["a", "b", "c", "b", "d"].index("b") 1 指定された文字列の位置を返す。
見つからなかった場合は例外(ValueError)が送出される。
Seq("a", "b", "c").indexOf("b")
Seq("a", "b", "c").lastIndexOf("b")
コピー list1 = ["a", "b", "c"]
list2 = list1.copy()
["a", "b", "c"] リストをコピーする。(内容が同じで新しいインスタンスを作る)
list1[:]」と同じ。
val list1 = Array("a", "b", "c")
val list2 = list1.clone()
部分 list1 = ["a", "b", "c", "d", "e"]
list1[1]
'b' 添字を指定して要素を取得する。 val list1 = Seq("a", "b", "c", "d", "e")
list1(1)
list1.apply(1)
list1[0] 'a' 0を指定すると先頭の要素が取得できる。 list1.head
list1[-1] 'e' -1を指定すると末尾の要素が取得できる。 list1.last
list1[1:3] ['b', 'c'] リストの一部分を取得する。(スライス記法 list1.slice(1, 3)
list1.view(1, 3)
list1[1:] ['b', 'c', 'd', 'e'] 先頭要素を取り除いたリストを取得する。(スライス記法 list1.tail
list1[:-1] ['a', 'b', 'c', 'd'] 末尾要素を取り除いたリストを取得する。(スライス記法 list1.init
結合 ["a", "b"] + ["c", "d"] ['a', 'b', 'c', 'd'] リスト同士の結合は「+」演算子を使う。 Seq("a", "b") ++ Seq("c", "d")
["a", "b"] * 3 ['a', 'b', 'a', 'b', 'a', 'b'] 「*」演算子で、リストを指定回数繰り返したリストになる。  
",".join(["a", "b", "c"]) 'a,b,c' 文字列のリストを区切り文字付きで結合する。 Seq("a", "b", "c").mkString(",")
更新 list1 = ["a", "b", "c"]
list1[1] = "z"
['a', 'z', 'c'] リストの要素の値を変更する。 val list1 = Array("a", "b", "c")
list1(1) = "z"
list1.update(1, "z")
list1 = ["a", "b", "c", "d", "e"]
list1[1:4] = ["x", "y"]
['a', 'x', 'y', 'e'] リストの一部分を変更する。(スライス記法  
追加 list1 = ["a", "b", "c"]
list1.append("z")
['a', 'b', 'c', 'z'] リストの末尾に値を追加する。
list1[len(list1):] = "z"」と同じ。
list1 += "z"」とも書ける。
val list1 = ArrayBuffer("a", "b", "c")
list1 += "z"
list1 = ["a", "b", "c"]
list1.extend(["x", "y"])
['a', 'b', 'c', 'x', 'y'] リストの末尾に値を追加する。
値がシーケンスだった場合はシーケンス内の要素が追加される。
val list1 = ArrayBuffer("a", "b", "c")
list1 ++= Seq("x", "y")
list1 = ["a", "b", "c"]
list1 += ["x", "y"]
['a', 'b', 'c', 'x', 'y']
list1 = ["a", "b"]
list1 *= 3
['a', 'b', 'a', 'b', 'a', 'b'] 指定回数繰り返したリストに更新する。  
挿入 list1 = ["a", "b", "c"]
list1.insert(1, "z")
['a', 'z', 'b', 'c'] リストに要素を挿入する。
list1[1:1] = "z"」と同じ。
val list1 = ArrayBuffer("a", "b", "c")
list1.insert(1, "z")
list1 = ["a", "b", "c"]
list1[1:1] = ["x", "y"]
['a', 'x', 'y', 'b', 'c'] リストにシーケンスを挿入する。 val list1 = ArrayBuffer("a", "b", "c")
list1.insertAll(1, Seq("x", "y"))
削除 list1 = ["a", "b", "c", "d"]
del list1[1:3]
['a', 'd'] 指定された位置の要素を削除する。(スライス記法 val list1 = ArrayBuffer("a", "b", "c", "d")
list1.remove(1, 2)
list1 = ["a", "b", "c"]
list1.remove("b")
['a', 'c'] 指定された値を削除する。
同じ値が複数あった場合は1個だけ削除される。
val list1 = ArrayBuffer("a", "b", "c")
list1 -= "b"
list1 = ["a", "b", "c"]
p = list1.pop(1)
list1=['a', 'c']
p='b'
指定された位置の値を返し、その位置の値を削除する。
位置を指定しなかった場合は末尾が対象。
val list1 = ArrayBuffer("a", "b", "c")
val p = list1.remove(1)
list1 = ["a", "b", "c"]
p = list1.pop()
list1=['a', 'b']
p='c'
 
list1.clear() [] 全要素を削除する。
del list1[:]」と同じ。
list1.clear()
ソート list1 = [2, 1, 4, 3]
list1.sort()
[1, 2, 3, 4] ソートする。  
list1 = [2, 1, 4, 3]
list1.sort(reverse=True)
[4, 3, 2, 1] 逆順ソートする。  
list1 = [("b", 3), ("a", 2), ("c", 1)]
list1.sort(key=lambda t: t[0])
[('a', 2), ('b', 3), ('c', 1)] 指定された関数でキーを抽出し、それに従ってソートする。  
sorted([2, 1, 4, 3]) [1, 2, 3, 4] ソートされたリストを返す。 Seq(2, 1, 4, 3).sorted
Seq("b"->3, "a"->2, "c"->1).sortBy(_._1)
逆順 list1 = [1, 2, 3]
list1.reverse()
[3, 2, 1] 順序を逆転させる。  
list(reversed([1, 2, 3])) [3, 2, 1] 順序を逆転させたイテレーターを返す。 Seq(1, 2, 3).reverse
関数型処理 list(filter(lambda n: n % 2 == 0, [1, 2, 3, 4])) [2, 4] リストの要素を条件に応じて抽出する。 Seq(1, 2, 3, 4).filter(_ % 2 == 0)
list(map(lambda n: str(n), [1, 2, 3])) ['1', '2', '3'] リストの要素を変換する。 Seq(1, 2, 3).map(_.toString)
from functools import reduce
reduce(lambda m, n: m + n, [1, 2, 3, 4])
10 集約する。[2018-01-15] Seq(1, 2, 3, 4).reduce(_ + _)
list(zip([1, 2, 3], ["a", "b", "c"])) [(1, 'a'), (2, 'b'), (3, 'c')] 複数のリストの同じ位置同士のタプルのシーケンスを返す。 Seq(1, 2, 3) zip Seq("a", "b", "c")
a = [(1, "a"), (2, "b"), (3, "c")]
tuple(map(list, zip(*a)))
([1, 2, 3], ['a', 'b', 'c']) zipの反対[2018-01-13]
zipの引数に*を付けてアンパックすることでunzipと同様の効果になる。
Seq((1, "a"), (2, "b"), (3, "c")).unzip
list(enumerate(["a", "b", "c"])) [(0, 'a'), (1, 'b'), (2, 'c')] リストの各要素に連番を付けたタプルのシーケンスを返す。 Seq("a", "b", "c").zipWithIndex
all([1, 2, 3])
all([1, 0, 3])
True
False
全ての要素が真ならTrueを返す。(空リストはTrue) Seq(1, 2, 3).forall(_ != 0)
any([1, 0, 3])
any([0, 0, 0])
True
False
いずれかの要素が真ならTrueを返す。(空リストはFalse) Seq(1, 0, 3).exists(_ != 0)
min([2, 1, 4, 3]) 1 最小値を返す。 Seq(2, 1, 4, 3).min
min([("a", 2), ("b", 1), ("c", 4), ("d", 3)], key=lambda t: t[1]) ('b', 1) Seq("a"->2, "b"->1, "c"->4, "d"->3).minBy(_._2)
max([2, 1, 4, 3]) 4 最大値を返す。 Seq(2, 1, 4, 3).max
max([("a", 2), ("b", 1), ("c", 4), ("d", 3)], key=lambda t: t[1]) ('c', 4) Seq("a"->2, "b"->1, "c"->4, "d"->3).maxBy(_._2)
sum([2, 1, 4, 3]) 10 合計値を返す。 Seq(2, 1, 4, 3).sum
型変換 list(range(3))
list((1, 2, 3))
list({1, 2, 3})
list({"a": 1, "b": 2, "c": 3})
[0, 1, 2]
[1, 2, 3]
[1, 2, 3]
['a', 'b', 'c']
他のシーケンス(range, タプル等)やset, dictをlistに変換できる。
(dictの場合はキーのリストになる)
Range(0, 3).toList
(1, 2, 3).productIterator.toList
Set(1, 2, 3).toList
Map("a"->1, "b"->2, "c"->3).map(_._1).toList

リスト内包表記

リストのリテラルは角括弧内に値をカンマ区切りで記述していくが、
値を並べる代わりに式で値を表現する書き方ができ、これをリスト内包表記(list comprehensions)と言う。
(JavaのStream.generate()あるいはStream.iterate()相当)

[ 式 for 変数 in ジェネレーター ]
リスト内包表記の例
例の結果 説明 Scala相当
[i * 2 for i in range(5)] [0, 2, 4, 6, 8]   for (i <- Range(0, 5)) yield i * 2
for (i <- 0 until 5) yield i * 2
Range(0, 5).map(_ * 2)
["a" for _ in range(3)] ['a', 'a', 'a'] 変数を使わない場合は「_」にしておくことが出来る。 for (_ <- 0 until 3) yield "a"
Seq.fill(3)("a")
[i for i in range(10) if i % 3 != 0] [1, 2, 4, 5, 7, 8] ifでフィルター(条件)を書くことが出来る。 for (i <- 0 until 10 if i % 3 != 0) yield i
Range(0, 10).filter(_ % 3 != 0)
[(i, j) for i in [1, 2] for j in ["a", "b"]] [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')] 二重ループにすることも出来る。 for (i <- Seq(1, 2); j <- Seq("a", "b")) yield (i, j)
[(i, j) for i in range(3) if i % 2 ==0 for j in range(4) if j % 2 != 0] [(0, 1), (0, 3), (2, 1), (2, 3)] 二重ループでifを付ける例。 for (i <- 0 until 3 if i % 2 == 0; j <- 0 until 4 if j % 2 != 0) yield (i, j)

ちなみに、setやdictでも内包表記を使う事が出来る。

リスト以外の内包表記の例
例の結果 説明 Scala相当
{i * 2 for i in range(5)} {0, 2, 4, 6, 8} setの内包表記。 (for (i <- 0 until 5) yield i * 2).toSet
// Range(0, 5).map(_ * 2).toSet
{str(i): i for i in range(3)} {'0': 0, '1': 1, '2': 2} dictの内包表記。
キーと値をコロン「:」で区切る。
(for (i <- 0 until 3) yield i.toString->i).toMap
{t[1]: t[0] for t in enumerate(["a", "b", "c"])} {'a': 0, 'b': 1, 'c': 2} (for (t <- Seq("a", "b", "c").zipWithIndex) yield t).toMap
// Seq("a", "b", "c").zipWithIndex.toMap
{t for t in zip(["a", "b"], [1, 2])} {('a', 1), ('b', 2)} タプルでキーと値を表現したつもりでも、setになる^^;
dictにしたければ「dict(zip(["a", "b"], [1, 2]))」。
(for (t <- Seq("a", "b") zip Seq(1, 2)) yield t).toSet
// (for (t <- Seq("a", "b") zip Seq(1, 2)) yield t).toMap
// Seq("a", "b").zip(Seq(1, 2)).toMap
g = (i * 2 for i in range(3))
for i in g: print(i)
0
2
4
ジェネレーターの生成。
(遅延評価される、一度しか評価できないシーケンス?)
val g = Iterator.from(0).take(3).map(_ * 2)
for (i <- g) println(i)
// g.foreach(println)

Pythonへ戻る / 技術メモへ戻る
メールの送信先:ひしだま