おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

Python初心者のお勉強ノート(3)リスト

リストは他の言語では配列といっているものに似ています。 一列に要素が並んだもので、その要素はどのような型でもOKです。 リストは角のあるカッコ[と]で要素を囲みます。

a = ["Hello", "world"]
b = [1, 2, 3, 4, 5]

変数aには2つの文字列"Hello"と"world"の要素を持つリストが代入され、変数bには5つの数値を要素に持つリストが代入されています。 要素の型は任意なので、文字列と数値の混在するリストも作れます。 ですが、同一の型のリストを使うことが多いでしょう。

インデックス

このとき、各要素は0から始まる番号(インデックス)を使って参照できます。 以下はPythonを起動し、プロンプトが出た状態からの入出力を表しています。

>>> a = ["Hello", "world"]
>>> b = [1, 2, 3, 4, 5]
>>> a[0]
'Hello'
>>> a[1]
'world'
>>> a[2]
Traceback (most recent call last):
  File "<python-input-4>", line 1, in <module>
    a[2]
    ~^^^
IndexError: list index out of range
>>> a[-1]
'world'
>>> a[-2]
'Hello'
>>> a[-3]
Traceback (most recent call last):
  File "<python-input-7>", line 1, in <module>
    a[-3]
    ~^^^^
IndexError: list index out of range
>>>

要素が2個なので、0または1で要素を参照することができ、2以上をインデックスにするとエラーになります。 また、負の数で後ろから参照することもでき、a[-1]とa[1]が後ろの要素、a[-2]とa[0]が前の要素を表します。 -3以下のインデックスはエラーになります。

リストの要素の個数を「リストの長さ」ということもあります。 負のインデックスは、それにリストの長さを加えて非負のインデックスに直せます。

a[-1] = a[-1+2] = a[1]
a[-2] = a[-2+2] = a[0]

変数bも、もちろんインデックスで参照でき、インデックスの範囲は-5から4までの整数です。

リストは可変

リストは可変です。 つまり、その要素を別のものに取り換えることができます。 インデックスと代入のイコールを用います。

>>> a
['Hello', 'world']
>>> a[1] = "there"
>>> a
['Hello', 'there']
>>>

a[1]はリストの前から2番目の要素でした。 代入によって、その要素を"there"に変更しています。

可変であるという性質はバグを誘発しやすいです。

>>> a = ["Hello", "world"]
>>> b = a
>>> a
['Hello', 'world']
>>> b
['Hello', 'world']
>>> a[1] = "there"
>>> a
['Hello', 'there']
>>> b
['Hello', 'there']
>>>

この例では、変数aと変数bには同じリストが代入されています。 そのため、aのリストの要素を変更すると、それはbのリストも変更されるのです。 つまり、aとbのリストは別物ではなく、同一のものなのです。

このことから、変数はオブジェクト(リスト、数値、文字列などはすべてオブジェクトの一種です)を指していると考えるのが良いことがわかります。 C言語でいうポインタのようなものです。

変数とオブジェクトの関係

上の例では2つの変数に同一のオブジェクトを代入しましたが、このようなプログラムを書くことはほとんどありません。 しかし、関数やメソッド(説明は後の記事で)の引数にオブジェクトを渡す場合、関数やメソッドの内側と外側が同じオブジェクトを共有することになり、事実上2つの変数に同じオブジェクトを代入したのと同じことになります。 この問題はまた別の記事で考えたいと思います。

スライス

スライスは、リストの一部を取り出して作った新たなリストを返します。 例を見てください。

>>> a = [1,2,3,4,5,6,7,8,9,10]
>>> a[3:5]
[4, 5]
>>> a[0:10:2]
[1, 3, 5, 7, 9]
>>> a[0:1]
[1]
>>>

aには10個の要素を持つリストが代入されています。

a[3:5]では、インデックスが3の要素からインデックスが5の要素の1つ前までが取り出されるので、[4, 5]が返されます。 インデックスが0から始まることに注意してください。

a[0:10:2]ではインデックスが0から10の前までです。 ただし、最後に2があり、これはステップと呼ばれます。インデックスを2ずつ増やしながら、スライスします。

  • 最初のインデックスは0
  • 次のインデックスは2だけ増えて2になる
  • これを繰り返すと、0, 2, 4, 6, 8と増えていく
  • 次は10だが、スライスは10の前までなので、これは含まれない
  • インデックスが0, 2, 4, 6, 8の要素は1, 3, 5, 7, 9なので、[1, 3, 5, 7, 9]が返される

ステップはマイナスも使えます。 ステップが負数だと後ろから前に進んでいくので、逆順になります。 ステップが省略された場合、そのデフォルト値は1です。

>>> a
[1, 2, 3, 4, 5]
>>> a[4:0:-1]
[5, 4, 3, 2]
>>> a[4:-1:-1]
[]
>>> a[4:-6:-1]
[5, 4, 3, 2, 1]
>>>

aは1から5までの整数のリストです。 インデックス4から0まで(0は除く)で、ステップが-1のスライスは、5から2までの配列で、aとは並びが逆順になります。 0番目の要素まで返したいと思い、a[4:-1:-1]としても、終了インデックスは-1+5=4に直されるので、ループの最初で終了してしまい、空のリストが返されます。 これを解決するには終了インデックスを-6にします。リストの長さ5を加えると、-6+5=-1なので、そこまでループが繰り返され、5から1までの整数のリストが返されます。

しかし、もう少し分かりやすい方法があります。 それは、開始と終了のインデックスを省略することです。 そのときは、それらは「端」になります。 ステップが正ならば開始は0、終了は「リストの長さ」で、ステップが負ならば開始は「リストの長さ-1」で終了は「-1」です。 この-1にリストの長さが加わることはありません。

>>> a
[1, 2, 3, 4, 5]
>>> a[::-1]
[5, 4, 3, 2, 1]
>>>

逆順に並べ替えた新しいリストを手に入れたいのであれば、この省略形を使うべきです。

スライスがa[x:y:z]であるとき、x, y, zを次のように呼びます。

  • x: 開始インデックス
  • y: 終了インデックス
  • z: ステップ

x, y, zはint型です。 大まかにいって、n = 0, 1, 2, 3, ... に対してi = x + nzを計算し0 <= i < lenとなるiに対するリストの要素a[i]をすべて集めた新しいリストを返します。 空のリストが返る可能性もあります。