2-2. リスト (list)

複数のデータを要素としてまとめて取り扱うデータとして、リストとタプルについて説明します。

参考

文字列を構成する要素は文字のみでしたが、リストでは構成する要素としてあらゆる型のデータを指定できます。 他のプログラミング言語では、リストに相当するものとして配列(もしくはアレイ)やベクターなどがあります。

リストを作成するには、リストを構成する要素をコンマで区切り全体をかぎ括弧 [...] で囲みます。

以下のセルでは数値を要素とするリストを作成して、変数に代入しています。 さらに、文字列と同様に組み込み関数 type を用いて、変数の値がリストであることを確認しています。

[1]:
numbers = [0, 10, 20, 30, 40, 50]
numbers
[1]:
[0, 10, 20, 30, 40, 50]
[2]:
type(numbers)
[2]:
list

リストのデータ型は list です。 (なお、後で見るように、list は他のデータをリストに変換する関数としても用いられます。)

次に文字列を構成要素とするリストを作成してみます。

[3]:
fruits = ['apple', 'banana', 'chelly']
fruits
[3]:
['apple', 'banana', 'chelly']

リストの要素としてあらゆる型のデータを指定でき、それらは混在してもかまいません。 以下のセルでは、数値と文字列が混在しています。

[4]:
numbers_fruits = [10, 'apple', 20, 'banana', 30]
numbers_fruits
[4]:
[10, 'apple', 20, 'banana', 30]

次のように、何も要素を格納していないリスト(空リスト)を作成できます。 空リストはプログラム実行の途中結果を記録する場合などによく使われています。 具体的な例については、後述する append メソッドの項を参照してください。

[5]:
empty=[]
empty
[5]:
[]

なお、[] を用いて空リストを作成するたびに、 常に新しいオブジェクト(それまでに作られたオブジェクトとは同一でないオブジェクト)が生成されます。 詳しくは「▲オブジェクトの等価性と同一性」を参照してください。

リストとインデックス

文字列の場合と同様に、インデックスを指定してリストの要素を取り出せます。 リストの x 番目の要素を取得するには次のような記法を用います。 インデックスは 0 から始まることに注意してください。

リスト[x-1]
[6]:
abcd = ['a', 'b', 'c', 'd']
abcd[2]
[6]:
'c'

文字列の場合とは異なり、リストは変更可能なデータです。 すなわちインデックスで指定されるリストの要素は、代入によって変更できます。

[7]:
abcd = ['a', 'b', 'c', 'd']
abcd[2] = 'hello'
abcd
[7]:
['a', 'b', 'hello', 'd']

文字列と同様に、スライスを使った範囲指定も可能です。

[8]:
abcd = ['a', 'b', 'c', 'd']
abcd[1:3]
[8]:
['b', 'c']
[9]:
abcd = ['a', 'b', 'c', 'd']
abcd[0:4:2]
[9]:
['a', 'c']

リストのスライスに対しては、代入も可能です。

[10]:
abcd = ['a', 'b', 'c', 'd']
abcd[1:3] = ['x', 'y', 'z']
abcd
[10]:
['a', 'x', 'y', 'z', 'd']

練習

リスト ln を引数として取り、ln の偶数番目のインデックスの値を削除したリストを返す関数 remove_evenindex を作成してください(ただし、0 は偶数として扱うものとします)。

ヒント:スライスを使います。

以下のセルの ... のところを書き換えて remove_evenindex(ln) を作成してください。

[11]:
def remove_evenindex(ln):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が全て True になることを確認してください。

[12]:
print(remove_evenindex(['a', 'b', 'c', 'd', 'e', 'f', 'g']) == ['b', 'd', 'f'] )
print(remove_evenindex([1, 2, 3, 4, 5]) == [2, 4])
False
False

多重リスト

リストの要素としてリストを指定することもできます。 リストを要素とするリストは多重リストと呼ばれます。 次は二重リストの例です。

[13]:
lns = [[1, 2, 3], [10, 20, 30], ['a', 'b', 'c']]

多重リストの要素指定は複数のインデックスで行います。 前の例で外側の [] で示されるリストの2番目の要素のリスト、 すなわち [10, 20, 30] の最初の要素は次のように指定します。

[14]:
lns[1][0]
[14]:
10

3番目のリストそのものを取り出したいときは、次のように指定します。

[15]:
lns[2]
[15]:
['a', 'b', 'c']

以下のようにリストの要素として、リストを値とする変数を指定することもできます。

[16]:
lns2 = [lns, ['x', 1, [11, 12, 13]], ['y', [100, 120, 140]] ]
lns2
[16]:
[[[1, 2, 3], [10, 20, 30], ['a', 'b', 'c']],
 ['x', 1, [11, 12, 13]],
 ['y', [100, 120, 140]]]
[17]:
lns2[0]
[17]:
[[1, 2, 3], [10, 20, 30], ['a', 'b', 'c']]

リストに対する関数・演算子・メソッド

リストの要素数

組み込み関数 len はリストの長さ、すなわち要素数、を返します。

[18]:
numbers = [0, 10, 20, 30, 40, 50]
len(numbers)
[18]:
6

maxmin

リストを引数とする関数は色々とあります。 関数 max は、数のリストが与えられると、その中の最大値を返します。 同様に関数 min はリストの中の最小値を返します。

[19]:
numbers = [30, 50, 10, 20, 40, 60]
max(numbers)
[19]:
60
[20]:
numbers = [30, 50, 10, 20, 40, 60]
min(numbers)
[20]:
10

maxmin は文字列のリストに対しても適用できます。文字列の比較は、いわゆる辞書順で行われます。

[21]:
characters = ['e', 'd', 'a', 'c', 'f', 'b']
min(characters)
[21]:
'a'

sum

関数 sum は、数のリストが与えられると、その要素の総和を返します。

[22]:
numbers = [30, 50, 10, 20, 40, 60]
sum(numbers)
[22]:
210
[23]:
sum([])
[23]:
0

リストと演算子

演算子 + によってリストの連結、* によって連結における繰り返し回数を指定することができます。

[24]:
numbers = [0, 10, 20, 30, 40, 50]
numbers + ['a', 'b', 'c']
[24]:
[0, 10, 20, 30, 40, 50, 'a', 'b', 'c']
[25]:
numbers*3
[25]:
[0, 10, 20, 30, 40, 50, 0, 10, 20, 30, 40, 50, 0, 10, 20, 30, 40, 50]

要素が全て同じ値(たとえば、0 )のリストを作る最も簡単な方法は、この * 演算子を使う方法です。

[26]:
zero10 = [0] * 10
zero10
[26]:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

*+ を繰り返し行うのと同じ結果を返します。 たとえば、x*3x+x+x と同じ結果を返します。

x が多重リストのとき、x の要素であるリストが y の中に複数回現れます。

[27]:
x = [[0, 1], [2, 3]]
y = x*3
y
[27]:
[[0, 1], [2, 3], [0, 1], [2, 3], [0, 1], [2, 3]]

このとき、x の要素が変更されると、y の中では複数箇所に変化が起こるので、注意してください。

[28]:
x[0][0] = 99
y
[28]:
[[99, 1], [2, 3], [99, 1], [2, 3], [99, 1], [2, 3]]

これは、y の中の複数個所にあるオブジェクトが同一だからです。 詳しくは、以下の「▲オブジェクトの等価性と同一性」を参照してください。

演算子 in は、左辺の要素がリストに含まれれば True を、それ以外では False を返します。

[29]:
10 in numbers
[29]:
True

リストに対する in 演算子は、論理演算 or を簡潔に記述するのに用いることもできます。 たとえば、

a1 == 1 or a1 == 3 or a1 == 7:

a1 in [1, 3, 7]:

と同じ結果を得られます。 or の数が多くなる場合は、in を用いた方がより読みやすいプログラムを書くことができます。

[30]:
a1 = 1
print(a1 == 1 or a1 == 3 or a1 == 7, a1 in [1, 3, 7])
a1 = 3
print(a1 == 1 or a1 == 3 or a1 == 7, a1 in [1, 3, 7])
a1 = 5
print(a1 == 1 or a1 == 3 or a1 == 7, a1 in [1, 3, 7])
True True
True True
False False

in 演算子は、左辺の要素がリストに含まれるかどうかを、 リストの要素を最初から順に調べることで判定しています。 したがって、リストの長さに比例した時間がかかります。 つまり、リストの長さが大きければ、それなりの時間がかかることに注意してください。

not in 演算子は、in 演算子の逆を意味します。

[31]:
10 not in numbers
[31]:
False
[32]:
11 not in numbers
[32]:
True

指定した要素のインデックス取得と数えあげ

index メソッドは引数で指定した要素のインデックスの番号を返します。 文字列には index に加えてこれと似た find メソッドもありましたが、リストでは使えません。

[33]:
numbers = [0, 10, 20, 30, 40, 50]
numbers.index(20)
[33]:
2

count メソッドは指定した要素の数を返します。

[34]:
all20 = [20]*3
all20.count(20) # 指定した要素のリスト内の数
[34]:
3

並べ替え(sort メソッド)

sort メソッドはリスト内の要素を並べ替えます。 引数に何も指定しなければ昇順でとなります。

[35]:
numbers = [30, 50, 10, 20, 40, 60]
numbers.sort()
[36]:
numbers
[36]:
[10, 20, 30, 40, 50, 60]
[37]:
characters = ['e', 'd', 'a', 'c', 'f', 'b']
characters.sort()
characters
[37]:
['a', 'b', 'c', 'd', 'e', 'f']

reverse = True オプションを指定すれば、要素を降順に並べ替えることもできます。 (これは 3.3 のキーワード引数と呼ばれるものですが、ここでは天下り的に、 並べ替えの方法を指定する情報と理解しておいてください。)

[38]:
numbers = [30, 50, 10, 20, 40, 60]
numbers.sort(reverse = True)
numbers
[38]:
[60, 50, 40, 30, 20, 10]

並べ替え(sorted 組み込み関数)

関数 sorted ではリストを引数に取って、そのリスト内の要素を昇順に並べ替えた結果をリストとして返します。

sorted(リスト)
[39]:
numbers = [30, 50, 10, 20, 40, 60]
sorted(numbers)
[39]:
[10, 20, 30, 40, 50, 60]

文字列の比較は、いわゆる辞書順で行われます。

[40]:
characters = ['e', 'd', 'a', 'c', 'f', 'b']
sorted(characters)
[40]:
['a', 'b', 'c', 'd', 'e', 'f']

sorted においても、 reverse = True と記述することで要素を降順に並べ替えることができます。

[41]:
numbers = [30, 50, 10, 20, 40, 60]
sorted(numbers, reverse=True)
[41]:
[60, 50, 40, 30, 20, 10]

ついでですが、多重リストをソートするとどのような結果が得られるか確かめてみてください。

[42]:
lns = [[20, 5], [10, 30], [40, 20], [30, 10]]
lns.sort()
lns
[42]:
[[10, 30], [20, 5], [30, 10], [40, 20]]

破壊的(インプレース)な操作と非破壊的な生成

上記では、sort メソッドと sorted 関数を紹介しましたが、両者の使い方が異なることに気が付きましたか?

具体的には、sort メソッドでは元のリストが変更されています。 一方、sorted 関数では元のリストはそのままになっています。もう一度確認してみましょう。

[43]:
numbers = [30, 50, 10, 20, 40, 60]
numbers.sort()
print('sortメソッドの実行後の元のリスト:', numbers)
numbers = [30, 50, 10, 20, 40, 60]
sorted(numbers)
print('sorted関数の実行後の元のリスト:', numbers)
sortメソッドの実行後の元のリスト: [10, 20, 30, 40, 50, 60]
sorted関数の実行後の元のリスト: [30, 50, 10, 20, 40, 60]

このように、sort メソッドは元のリストを変更してしまいます。 このような操作を破壊的あるいはインプレース (in-place) であるといいます。

一方、sorted 関数は新しいリストを生成し元のリストを破壊しません、このような操作は非破壊的であるといいます。

sorted 関数を用いた場合、その返値(並べ替えの結果)は新しい変数に代入して使うことができます。

一方、sort メソッドはリストを返さないためそのような使い方はできません。

[44]:
numbers = [30, 50, 10, 20, 40, 60]
numbers1 = sorted(numbers)
print('sorted関数の返値:', numbers1)

numbers = [30, 50, 10, 20, 40, 60]
numbers2 = numbers.sort()
print('sortメソッドの返値:', numbers2)
sorted関数の返値: [10, 20, 30, 40, 50, 60]
sortメソッドの返値: None

リストを操作するメソッドなど

ここからはリストを操作するためのメソッドなどを紹介していきます。

メソッドや組み込み関数が破壊的であるかどうかは、一般にその名称などからは判断できません。 それぞれ破壊的かどうか理解してから利用しなければなりません。

なお、次の append メソッド以外は、必要に応じて参照すればよく、 それ以降タプルの項まで飛ばして構いません。

リストに要素を追加する

append メソッドはリストの最後尾に指定した要素を付け加えます。

リスト.append(追加する要素)
[45]:
numbers = [10, 20, 30, 40, 50]
numbers.append(100)
numbers
[45]:
[10, 20, 30, 40, 50, 100]

append は、上述した空のリストと組み合わせて、あるリストから特定の条件を満たす要素のみからなる新たなリストを構成する、というような状況でしばしば用いられます。たとえば、リスト numbers1 = [10, -10, 20, 30, -20, 40, -30] から 0 より大きい要素のみを抜き出したリスト positives は次のように構成することができます。

[46]:
numbers1 = [10, -10, 20, 30, -20, 40, -30]
positives = [] # 空のリストを作成する
positives.append(numbers1[0])
positives.append(numbers1[2])
positives.append(numbers1[3])
positives.append(numbers1[5])
positives
[46]:
[10, 20, 30, 40]

▲リストにリストの要素を追加する

extend メソッドはリストの最後尾に指定したリストの要素を付け加えます。

リスト.extend(追加するリスト)
[47]:
numbers = [10, 20, 30, 40, 50]
numbers.extend([200, 300, 400, 200]) # numbers += [200, 300, 400, 200] と同じ
numbers
[47]:
[10, 20, 30, 40, 50, 200, 300, 400, 200]

▲リストに要素を挿入する

insert メソッドはリストのインデックスを指定した位置に新しい要素を挿入します。

リスト.insert(インデックス, 新しい要素)
[48]:
numbers = [10, 20, 30, 40, 50]
numbers.insert(1, 1000)
numbers
[48]:
[10, 1000, 20, 30, 40, 50]

▲リストから要素を削除する

remove メソッドは指定した要素をリストから削除します。

リスト.remove(削除したい要素)

ただし、指定した要素が複数個リストに含まれる場合、一番最初の要素が削除されます。また、指定した値がリストに含まれない場合はエラーとなります。

[49]:
numbers = [10, 20, 30, 40, 20]
numbers.remove(30) # 指定した要素を削除
numbers
[49]:
[10, 20, 40, 20]
[50]:
numbers.remove(20) # 指定した要素が複数個リストに含まれる場合、一番最初の要素を削除
numbers
[50]:
[10, 40, 20]
[51]:
numbers.remove(100) # リストに含まれない値を指定するとエラー
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/tmp/ipykernel_2395/682954414.py in <module>
----> 1 numbers.remove(100) # リストに含まれない値を指定するとエラー

ValueError: list.remove(x): x not in list

▲リストからインデックスで指定した要素を削除する

pop メソッドはリストから指定したインデックスを削除し、その要素を返します。

リスト.pop(削除したい要素のインデックス)
[52]:
numbers = [10, 20, 20, 30, 20, 40]
print(numbers.pop(3))
print(numbers)
30
[10, 20, 20, 20, 40]

インデックスを指定しない場合、最後尾の要素を削除して返します。

リスト.pop()
[53]:
ln = [10, 20, 30, 20, 40]
print(ln.pop())
print(ln)
40
[10, 20, 30, 20]

▲リスト要素を削除する

del 文は指定するリストの要素を削除します。具体的には以下のように削除したい要素をインデックスで指定します。
del も破壊的であることに注意してください。
del リスト[x]
[54]:
numbers = [10, 20, 30, 40, 50]
del numbers[2]
numbers
[54]:
[10, 20, 40, 50]

スライスを使うことも可能です。

del リスト[x:y]
[55]:
numbers = [10, 20, 30, 40, 50]
del numbers[2:4]
numbers
[55]:
[10, 20, 50]

▲リストの要素を逆順にする

reverse メソッドはリスト内の要素の順序を逆順にします。

[56]:
characters = ['e', 'd', 'a', 'c', 'f', 'b']
characters.reverse()
characters
[56]:
['b', 'f', 'c', 'a', 'd', 'e']

copy

リストを複製します。 すなわち、ln の値がリストであるとき、ln.copy()ln と同じ長さのリストを新たに作って、 ln の要素を新しいリストに同じ順番で格納して、その新しいリストを返します。

複製されたリストに変更を加えたとしても、もとのリストは影響を受けません。

[57]:
numbers = [10, 20, 30, 40, 50]
numbers2 = numbers.copy()
del numbers[1:3]
numbers.reverse()
print(numbers)
print(numbers2)
[50, 40, 10]
[10, 20, 30, 40, 50]

一方、代入を用いた場合には影響を受けることに注意してください。

[58]:
numbers = [10, 20, 30, 40, 50]
numbers2 = numbers
del numbers[1:3]
numbers.reverse()
print(numbers)
print(numbers2)
[50, 40, 10]
[50, 40, 10]

リストと文字列の相互変換

文字列は変更不可能である一方、リスト変更可能です。 そのため、文字列処理をする際は、文字列のリストに一旦変換してから、変更を加えて、文字列に変換することが典型的です。 ここでは、文字列とリストの相互変換の方法を示します。

まず、文字列 slist 関数に渡すと、s を文字単位で区切ったリストが得られます。

[59]:
list('abc123')
[59]:
['a', 'b', 'c', '1', '2', '3']

文字単位ではなく、指定された文字列で区切ってリストにする際は、 split メソッドを使います。

[60]:
'banana'.split('n')
[60]:
['ba', 'a', 'a']
[61]:
'A and B and C'.split(' and ')
[61]:
['A', 'B', 'C']

split を無引数で呼び出すと、連続した空白文字を区切りと見做します。

[62]:
'A   B\nC  '.split()
[62]:
['A', 'B', 'C']

逆に、文字のリストを連結して1つの文字列にする際は、 join メソッドを次のように使います。

接合点に挿入する文字列.join(文字列のリスト)
[63]:
''.join(['a', 'b', 'c', '1', '2', '3'])
[63]:
'abc123'
[64]:
'n'.join(['ba', 'a', 'a'])
[64]:
'banana'

なお、 join の引数は、文字列のリストだけでなく、文字列のタプルでも問題ありません。 タプルについては、次で述べます。

練習

emailアドレス email とドメイン名 domain を引数に取って、email のドメイン名を domain に置き換える関数 change_domain(email, domain) を作成してください。 なお、emailアドレスのドメイン名とは、 '@' で区切られた右側の部分を意味します。

次のセルの ... のところを書き換えて change_domain(email, domain) を完成させてください。

[65]:
def change_domain(email, domain):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[66]:
print(change_domain('spam@utokyo-ipp.org', 'ipp.u-tokyo.ac.jp') == 'spam@ipp.u-tokyo.ac.jp')
False

タプル (tuple)

タプルは、リストと同じようにデータの並びであり、あらゆる種類のデータを要素にできます。 ただし、リストと違ってタプルは一度設定した要素を変更できません(文字列も同様でした)。 すなわち、タプルは変更不可能なデータです。 したがって、リストの項で説明したメソッドの多く、要素を操作するもの、は適用できないのですが、 逆にいうと、作成した後で要素を変更する必要がない場合は、 タプルの方が実装の効率がよいので、リストよりもタプルを使うべきです。

たとえば、関数が複数の値をリストにして返し、呼び出し側がすぐにリストをばらばらにして値を取り出すような場合は、 リストよりもタプルを使うべきです。 また、平面上の点を表そうとするとき、x座標とy座標を別々に変化させる必要がなければ、 (3, 5) のようなタプルを使うのが自然です。 このように、タプルを作成するには数学におけるのと同様に要素を丸括弧 (...) で囲みます。

例を見ましょう。

[67]:
x = 3
y = 5
point = (x, y)
[68]:
point
[68]:
(3, 5)
[69]:
type(point)
[69]:
tuple
[70]:
numbers3 = (1, 2, 3)
numbers3
[70]:
(1, 2, 3)

実は、丸括弧なしでもタプルを作成できます。

[71]:
numbers3 = 1,2,3
numbers3
[71]:
(1, 2, 3)

要素が1つだけの場合は、 t = (1) ではなく、次のようにします。

[72]:
onlyone = (1,)
onlyone
[72]:
(1,)

t = (1) だと、t = 1 と同じになってしまいます。

[73]:
onlyone = (1)
onlyone
[73]:
1

何も要素を格納していないタプル(空タプル)は () で作成できます。

[74]:
empty = ()
empty
[74]:
()

リストや文字列と同様に、インデックスや組み込み関数を使った操作が可能です。

[75]:
numbers3 = (1, 2, 3)
numbers3[1] # インデックスの指定による値の取得
[75]:
2
[76]:
len(numbers3) # lenはタプルを構成する要素の数
[76]:
3
[77]:
numbers3[1:3] # スライス
[77]:
(2, 3)

上述しましたが、一度作成したタプルの要素を後から変更することはできません。 したがって以下のプログラムはエラーとなります。

numbers3 = (1, 2, 3)
numbers3[1] = 5

組み込み関数 list を使って、タプルをリストに変換できます。 (list はリストのデータ型でもあります。)

[78]:
numbers3 = (1, 2, 3)
list(numbers3)
[78]:
[1, 2, 3]

組み込み関数 tuple を使って、逆にリストをタプルに変換できます。 (tuple はタプルのデータ型でもあります。)

[79]:
numbers2 = [1, 2]
tuple(numbers2)
[79]:
(1, 2)

練習

整数の要素からなるリスト ln を引数として取り、 ln に含まれる要素を逆順に格納したタプルを返す関数 reverse_totuple を作成してください。

以下のセルの ... のところを書き換えて reverse_totuple(ln) を作成してください。

[80]:
def reverse_totuple(ln):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[81]:
print(reverse_totuple([1, 2, 3, 4, 5]) == (5, 4, 3, 2, 1))
False

多重代入

多重代入では、左辺に複数の変数などを指定してタプルやリストの全ての要素を一度の操作で代入することができます。

[82]:
numbers = [0, 10, 20, 30, 40]
[a, b, c, d, e] = numbers
b
[82]:
10

以下のようにしても同じ結果を得られます。

[83]:
a, b, c, d, e = numbers
b
[83]:
10

多重代入は文字列に対しても実行可能です。

[84]:
a, b, c, d, e = 'hello'
d
[84]:
'l'

タプルに対しても実行可能です。

[85]:
numbers3 = (1, 2, 3)
(x,y,z) = numbers3
y
[85]:
2

以下のように様々な書き方が可能です。

[86]:
x,y,z = numbers3
print(y)
(x,y,z) = (1, 2, 3)
print(y)
x,y,z = (1, 2, 3)
print(y)
(x,y,z) = 1, 2, 3
print(y)
x,y,z = 1, 2, 3
print(y)
2
2
2
2
2

多重代入を使うことで、2つの変数に格納された値の入れ替えを行う手続きはしばしば用いられます。

[87]:
x = 'apple'
y = 'pen'
x, y = y, x
print(x, y) #w = x; x = y; y = w と同じ結果が得られる
pen apple

リストやタプルの比較演算

数値などを比較するのに用いた比較演算子を用いて、2つのリストやタプルを比較することもできます。

[88]:
print([1, 2, 3] == [1, 2, 3])
print([1, 2] == [1, 2, 3])
True
False
[89]:
print((1, 2, 3) == (1, 2, 3))
print((1, 2) == (1, 2, 3))
True
False
[90]:
print([1, 2, 3] != [1, 2, 3])
print([1, 2] != [1, 2, 3])
False
True
[91]:
print((1, 2, 3) != (1, 2, 3))
print((1, 2) != (1, 2, 3))
False
True

大小の比較は、いわゆる辞書式による比較で、リストやタプルの最初の要素から順に比較して大小を決めます。 片方がもう片方を拡張したものであれば、拡張した方を大きいとします。

[92]:
print([1, 2, 3] <= [1, 2, 3])
print([1, 2, 3] < [1, 2, 3])
print([1, 2, 3] < [1, 2, 4])
print([1, 2] < [1, 2, 3])
True
False
True
True
[93]:
print((1, 2, 3) <= (1, 2, 3))
print((1, 2, 3) < (1, 2, 3))
print((1, 2, 3) < (1, 2, 4))
print((1, 2) < (1, 2, 3))
True
False
True
True

for文による繰り返しとリスト・タプル

きまった操作の繰り返しはコンピュータが最も得意とする処理のひとつです。 リストのそれぞれの要素にわたって操作を繰り返したい場合はfor文を用います。

リスト ls の要素全てに対して、実行文 を繰り返すには次のように書きます。

for value in ls:
    実行文

for で始まる行の in の後に処理対象となるリスト ls が、in の前に変数 value が書かれます。

ls の最初の要素、すなわち ls[0]value に代入され 実行文 が処理されます。 実行文 の処理が終われば、ls の次の要素が value に代入され、処理が繰り返されます。 このようにして、ls の要素に対する処理が len(ls) 回繰り返されると、for文の処理が終了します。

ここでの in の働きは、先に説明したリスト要素の有無を検査する in とは異なることに、 そして、if文と同様、 実行文 の前にはスペースが必要であることに注意してください。

次に具体例を示します。 3つの要素を持つリスト ls から1つずつ要素を取り出し、変数 value に代入しています。 実行文 では value を用いて取り出した要素を参照しています。

[94]:
ls = [0,1,2]

for value in ls:
    print('For loop:', value)
For loop: 0
For loop: 1
For loop: 2

in の後に直接リストを記述することもできます。

[95]:
for value in [0,1,2]:
    print('For loop:', value)
For loop: 0
For loop: 1
For loop: 2

実行文 の前にスペースがないとエラーが出ます。

[96]:
for value in  [0,1,2]:
print('For loop:', value)
  File "/tmp/ipykernel_2395/3755322112.py", line 2
    print('For loop:', value)
    ^
IndentationError: expected an indented block after 'for' statement on line 1

エラーが出れば意図した通りにプログラムが組めていないのにすぐ気が付きますが、 エラーが出ないために意図したプログラムが組めていないことに気が付かないことがあります。 たとえば、次のような内容を実行しようとしていたとします。

[97]:
for value in  [0,1,2]:
    print('During for loop:', value)
    print('During for loop, too:', value)
During for loop: 0
During for loop, too: 0
During for loop: 1
During for loop, too: 1
During for loop: 2
During for loop, too: 2

後者の print の行のスペースの数が間違ってると、次のような結果になる場合がありますので注意してください。

[98]:
for value in [0,1,2]:
    print('During for loop:', value)
print('During for loop, too:', value) #この行のスペースの数が間違っていたがエラーは出ない
During for loop: 0
During for loop: 1
During for loop: 2
During for loop, too: 2

タプルの要素にまたがる処理もリストと同様に行えます。

[99]:
for value in (0,1,2):
    print('For loop:', value)
For loop: 0
For loop: 1
For loop: 2

以下はリストに対するfor文の典型例です。numbers は数のリストとします。

[100]:
numbers = [0,1,2,3,4,5]

以下のようにして、このリストの要素の自乗から成るリストを求めることができます。

[101]:
squares1 = []
for x in numbers:
    squares1.append(x**2)
squares1
[101]:
[0, 1, 4, 9, 16, 25]

squares1 には最初に空リストが代入されます。 そして、numbers の各要素の自乗がこのリストに次々と追加されます。

練習

整数の要素からなるリスト ln を引数として取り、ln の要素の総和を返す関数 sum_list を作成してください。

以下のセルの ... のところを書き換えて sum_list(ln) を作成してください。

[102]:
def sum_list(ln):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が全て True になることを確認してください。

[103]:
print(sum_list([10, 20, 30]) == 60)
print(sum_list([-1, 2, -3, 4, -5]) == -3)
False
False

for文による繰り返しと文字列

for文を使うと文字列全体にまたがる処理も可能です。 文字列 str1 をまたがって一文字ずつの繰り返し処理を行う場合は次のように書きます。 ここで、c には取り出された一文字(の文字列)が代入されています。

for c in str1:
    実行文

str1 で与えられる文字列を一文字ずつ大文字で出力する処理は以下のようになります。

[104]:
str1 = 'Apple and pen'
for c in str1:
    print(c.upper())
A
P
P
L
E

A
N
D

P
E
N

練習

ATGCの4種類の文字から成る文字列 str_atgc が引数として与えられたとき、次のようなリスト list_count を返す関数 atgc_countlist を作成してください。ただし、 list_count の要素は、各塩基 bp に対して str_atgc 中の bp の出現回数と bp の名前を格納した(長さ2の)リストとします。

ヒント:文字列 'ATGC' に対する繰り返しを用いることができます。

以下のセルの ... のところを書き換えて atgc_countlist(str_atgc) を作成してください。

[105]:
def atgc_countlist(str_atgc):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[106]:
print(sorted(atgc_countlist('AAGCCCCATGGTAA')) == sorted([[5, 'A'], [2, 'T'], [3, 'G'], [4, 'C']]))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_2395/2239165687.py in <module>
----> 1 print(sorted(atgc_countlist('AAGCCCCATGGTAA')) == sorted([[5, 'A'], [2, 'T'], [3, 'G'], [4, 'C']]))

TypeError: 'NoneType' object is not iterable

for文によるリスト初期化の短縮記法

先に、リストの要素の自乗から成るリストを求める例を説明しました。

[107]:
numbers = [0,1,2,3,4,5]

squares1 = []
for x in numbers:
    squares1.append(x**2)
squares1
[107]:
[0, 1, 4, 9, 16, 25]

詳しくは6-1で説明されますが、内包表記を用いて書き換えると、以下のように1行で書くことができます。

[108]:
squares2 = [x**2 for x in numbers]
squares2
[108]:
[0, 1, 4, 9, 16, 25]

すなわち、

[x を含む式 for x in リストを返す式]

という式は、"リストを返す式" が返したリストの各要素を x に代入して "x を含む式" を計算し、 その結果をリストにして返します。 もちろん、変数は x でなくてもよいです。

[109]:
[y**2 for y in numbers]
[109]:
[0, 1, 4, 9, 16, 25]

▲オブジェクトの等価性と同一性

1-3で、Pythonにおける値はオブジェクトと総称されますと述べました。 ここでは、オブジェクトの等価性と同一性について説明します。

既に見てきたように、演算子 == を用いてオブジェクトの等価性を判定できます。

[110]:
a = []
b = []

このとき、ab の値はどちらも空リストなので、以下のように a の値と b の値は等価です。

[111]:
a == b
[111]:
True

しかし、[ で始まり ] で終わる式を評価すると、 必ず新しいリスト(オブジェクト)が作られて返されるので、 ab の値は同一ではありません。

オブジェクトの同一性は演算子 is を用いて判定できます。

[112]:
a is b
[112]:
False

リストの要素はオブジェクトなので、要素ごとに等価性と同一性が定まります。

例として、ab を要素とするリスト c を作ります。

[113]:
c = [a, b]
c
[113]:
[[], []]
[114]:
c[0] is c[1]
[114]:
False

a を変化させてみましょう。

[115]:
a.append(1)
a
[115]:
[1]

すると c は以下のようになります。

[116]:
c
[116]:
[[1], []]

ここで、ab は等価でなくなりました。

[117]:
a == b
[117]:
False

次に、b を要素として二重に含むリスト d を作ります。

[118]:
d = [b, b]
d
[118]:
[[], []]
[119]:
d[0] is d[1]
[119]:
True

b を変化させてみましょう。

[120]:
b.append(1)
b
[120]:
[1]

すると d は以下のようになります。

[121]:
d
[121]:
[[1], [1]]

演算子 == でリストを比較すると、要素まで見て等価性を判定します。

[122]:
print(a, b)
a == b
[1] [1]
[122]:
True

演算子 == は、要素の比較も == で行います。

[123]:
print(c, d)
c == d
[[1], [1]] [[1], [1]]
[123]:
True

一方、オブジェクトの同一性は変化しません。

[124]:
a is b
[124]:
False

== の否定形は != で、is の否定形は is not です。 not x == yx != y と書けます。not x is yx is not y と書けます。 is not はこれで1つの演算子なので注意してください。

[125]:
c != d
[125]:
False
[126]:
a is not b
[126]:
True

練習の解答

[127]:
def remove_evenindex(ln):
    ln2 = ln[1::2]
    return ln2
#remove_evenindex(['a', 'b', 'c', 'd', 'e', 'f', 'g'])
[128]:
def change_domain(email, domain):
    return '@'.join([email.split('@')[0], domain])
[129]:
def reverse_totuple(ln):
    ln.reverse()
    tup = tuple(ln)
    return tup
#reverse_totuple([1, 2, 3, 4, 5])
[130]:
def sum_list(ln):
    int_sum = 0
    for value in ln:
        int_sum += value
    return int_sum
#sum_list([10, 20, 30])
[131]:
def atgc_countlist(str_atgc):
    list_count = []
    for value in 'ATGC':
        int_bpcnt = str_atgc.count(value)
        list_count.append([int_bpcnt, value])
    return list_count
#atgc_countlist('AAGCCCCATGGTAA')
[ ]: