まえおき
- この記事は執筆中のやさしくはじめるPythonプログラミングの本の特定の章の部分抜粋です。
- 入門本なので初心者の方向けです。
- ビルトインのリストの操作やメソッド関係の章の内容が主になります。
- Qiita記事にマッチしていない箇所(「章」や「ページ」といった単語が使っていたり、改行数が余分だったり、リンクが対応していない等)があるという点はご留意ください。気になる方は↑のリンクの電子書籍版をご利用ください(Githubでダウンロードできます)。
- コメントなどでフィードバックいただいた場合、書籍側にも活用・反映させていただく場合があります。
リストや辞書に含まれているかを調べる : in
任意の値 in 対象のリスト
というフォーマットでコードを書くと、その値がリスト内に含まれているかどうかの真偽値を取得することができます。
例えば以下のようなコードでは200という値がリスト内に含まれるかどうかの真偽値を取得しています(含まれるのでTrueが返却されます)。
list_value = [100, 200, 300]
is_in = 200 in list_value
print(is_in)
コード実行結果の出力内容:
True
以下のコードのように、リストに含まれない値(500)を指定した場合にはFalseが返却されます。
list_value = [100, 200, 300]
is_in = 500 in list_value
print(is_in)
コード実行結果の出力内容:
False
リストの末尾に値を追加する : appendメソッド
リストの最後に特定の値を追加するにはappendメソッドを使います。第一引数に追加したい要素を指定します。
返却値として要素が追加されたリストのコピーが返却されるのではなく、メソッドを実行したリスト自体が更新されます(返却値用の記述が設定されていない点に着目してください)。
これは、要素を追加する度にリストのコピーを作っているとリストが大きい場合に処理の負荷が増大してしまうためにこのようにコピーを作らない形になっています。
list_value = ['猫', '犬']
list_value.append('兎')
print(list_value)
コード実行結果の出力内容:
['猫', '犬', '兎']
特定の位置に要素を追加する : insertメソッド
appendメソッドではリストの末尾に要素が追加されましたが、insertメソッドではリスト内の任意の位置に要素を追加することができます。
第一引数には追加する位置のインデックス、第二引数に追加したい要素を指定します。
インデックスは0からスタートするので、先頭(1番目)に要素を追加したい場合には第一引数に0を指定します。
先頭(インデックス0)に兎という文字をリストへ追加するサンプル :
list_value = ['猫', '犬']
list_value.insert(0, '兎')
print(list_value)
コード実行結果の出力内容:
['兎', '猫', '犬']
2番目(インデックス1)に兎という文字をリストへ追加するサンプル :
list_value = ['猫', '犬']
list_value.insert(1, '兎')
print(list_value)
コード実行結果の出力内容:
['猫', '兎', '犬']
なお、リストの範囲を超える位置に要素を追加しようとした場合には末尾に追加されます。例えば2件しかないリストに対して4件目(インデックス3)や5件目(インデックス4)の位置に要素を追加しようとしても、3件目の位置に要素が存在しないため3件目の位置(インデックス2)に追加が実行されます。
list_value = ['猫', '犬']
list_value.insert(3, '兎')
print(list_value)
コード実行結果の出力内容:
['猫', '犬', '兎']
リストへ他のリストの内容を追加する : extendメソッド
2つのリストを統合して1つにしたいといったケースにはextendメソッドを使います。extendは「拡張する」といった意味を持つ単語になります。とあるリストに別のリストの値を追加して拡張(大きく)するといったところでしょうか。
追加したい方のリストでメソッドを実行し、追加したい値を格納したリストを第一引数に指定します。
追加された値は追加先のリストの値の後の位置に追加されます。
left_list = ['猫', '犬']
right_list = ['狼', '兎']
left_list.extend(right_list)
print(left_list)
コード実行結果の出力内容:
['猫', '犬', '狼', '兎']
引数へはリスト以外のタプルなども問題なく指定することができます。
left_list = ['猫', '犬']
right_tuple = ('狼', '兎')
left_list.extend(right_tuple)
print(left_list)
コード実行結果の出力内容:
['猫', '犬', '狼', '兎']
リストから要素を取り出す : popメソッド
popメソッドはリスト内の特定のインデックスの位置にある値を取り出します。popは「飛び出る」といった意味を持ち、リスト内の特定の要素が飛び出してくるイメージです。
メソッドの返却値は対象の要素の値で、実行後はその値はリストから消えてしまいます。
第一引数には対象の要素のインデックスの位置を指定します。インデックスに0を指定すれば先頭の要素が取り出され、インデックスに1を指定すれば2番目の要素が取り出されます。
以下のサンプルでは'猫', '犬', '兎'という文字の3件の値を格納しているリストで2番目(インデックス1)の'犬'の値を取り出しています。
取り出された値が'犬'になっている点と、その後のリストの中に'犬'の値が含まれていない点に着目してください。
list_value = ['猫', '犬', '兎']
popped_value = list_value.pop(1)
print('popped_value:', popped_value)
print('list_value:', list_value)
コード実行結果の出力内容:
popped_value: 犬
list_value: ['猫', '兎']
対象の要素をリストから取り除く : removeメソッド
removeメソッドはリスト内の特定の値の要素を取り除きます。popなどと同じようにリストの中身が減る結果となりますが、popとは異なりインデックスではなく値を指定して削除します。例えばリストから'犬'という値を削除したければ第一引数に'犬'と指定します。
list_value = ['猫', '犬', '兎']
list_value.remove('犬')
print(list_value)
コード実行結果の出力内容:
['猫', '兎']
削除対象として指定した引数の値がリスト内に含まれている場合には、先頭の値が削除されます。
以下のサンプルでは'犬'という値が2件リストに含まれている状態でremoveメソッドを実行しています。2番目の'犬'という値は結果のリストに残っていることが確認できます。
list_value = ['猫', '犬', '兎', '犬']
list_value.remove('犬')
print(list_value)
コード実行結果の出力内容:
['猫', '兎', '犬']
delキーワードを使ってリスト内の要素を削除する
removeやpopメソッドでもリスト内の特定の要素をリストから取り除くことができますが、del 対象のリスト[削除対象のインデックス]
という書き方で削除することもできます。
たとえばlist_value
というリストの変数の2番目の値(インデックス1)を削除したい場合にはdel list_value[1]
と書きます。
popのように値を取り出すのが不要(取り出した値をその後利用しない)で、且つ値ではなくインデックスを指定して削除したい場合にはこちらの書き方が適しています。
list_value = ['猫', '犬', '兎']
del list_value[1]
print(list_value)
コード実行結果の出力内容:
['猫', '兎']
リストの中身を空にする : clearメソッド
clearメソッドを使うとリストの中身を空にできます。
試しに以下のように3つの値を格納していたリストでclearメソッドを実行してみると、リストの中身が空([]
)になっていることが確認できます。
list_value = ['猫', '犬', '兎']
list_value.clear()
print(list_value)
コード実行結果の出力内容:
[]
なお、変数を空のリストにするだけであれば以下のように新たに空のリストを変数に設定するだけでも対応することができます。
list_value = ['猫', '犬', '兎']
list_value = []
print(list_value)
コード実行結果の出力内容:
[]
ではどちらを使うべきなのか?という点ですが、厳密に比較すると細かいところが異なりますが、最初は特にどちらを使っていただいても問題ありません。
以下に違いを一応記載しておきますが、この説明も最初はスキップいただいても読んでみて頭の片隅に置いていただいてもどちらでも構いません。
説明のためにリストという入れ物を段ボールの入れ物と例えてみます。
clearメソッドは段ボールは同じものを使いつつ、中身を全て出して空の状態の段ボールを作るような挙動になります。
一方で空のリストを変数に設定する対応は、別の空の段ボールをもう一つ用意するような制御になります(つまり、段ボールが2枚使われています)。
コードでもケースによっては差異が発生します。以下のようにリストを引数に受け付ける関数を二つ用意し(clear_listとassign_blank_list)、関数の中でそれぞれclearと空のリストの変数への反映をしています。
clearメソッドの方(clear_list関数)は前述の通り同じ段ボールを使って中身を空にする処理なので、関数実行後はそのリスト(list_value_1)は空になっています。
一方で関数内で別の空のリストを変数に設定した場合(assign_blank_list)は、関数内のローカル変数として2枚目の段ボールが割り振られています。そのため、関数の外の変数(1枚目の段ボールとしてのlist_value_2)と関数内のリストの変数(2枚目の段ボールとしてのlist_value)は別の値になります。
list_value_2の関数実行後の出力内容も空になっておらず、元の値を保持したままとなります。
def clear_list(list_value):
list_value.clear()
def assign_blank_list(list_value):
list_value = []
list_value_1 = ['猫', '犬', '兎']
clear_list(list_value=list_value_1)
print('list_value_1:', list_value_1)
list_value_2 = ['猫', '犬', '兎']
assign_blank_list(list_value=list_value_2)
print('list_value_2:', list_value_2)
コード実行結果の出力内容:
list_value_1: []
list_value_2: ['猫', '犬', '兎']
このように厳密に言うとそれぞれで挙動が微妙に異なります。ただし普段作業する上ではパフォーマンスなども含めてもどちらでも問題がないケースが大半ですので、最初はそこまで意識しなくても構いません。
リストをコピーする : copyメソッド
リストを中身ごとコピーするにはcopyメソッドを使います。このメソッドを呼び出すと返却値に新しいリストが設定されます。
以下のコードではコピー元のリストと同じ内容がコピー後のリストで出力されていることが確認できます。
list_value = ['猫', '犬']
copied_list_value = list_value.copy()
print(copied_list_value)
コード実行結果の出力内容:
['猫', '犬']
別の変数に同じリストの内容を設定するだけであれば以下のようにイコールの記号を指定するだけでも実現できます(先ほどと同じ出力結果となります)。
list_value = ['猫', '犬']
copied_list_value = list_value
print(copied_list_value)
コード実行結果の出力内容:
['猫', '犬']
ただし、clearメソッドのセクションで説明したものに近いですが、こちらもそれぞれで挙動が異なります。
clearメソッドの時と同じようにリストの入れ物を段ボール箱で例えてみると、copyメソッドは段ボールの中身ごと段ボールをコピーする挙動になります。
一方でイコールの記号を使って別の変数にリストの値を設定する場合、段ボールは一つだけで同じものを使いつつ別の呼び方をする(複数の変数名を付ける)という挙動になります。
この挙動はclearメソッドの時よりも顕著に違いが出てきます(普段からコードを書く際に意識する必要が発生します)。
コードで差を確認してみましょう。
まずはcopyメソッドを使う形のものからです。これは段ボール箱の中身ごとコピーする処理なので、コピー後のリストの内容を更新してもコピー前のリストの内容は箱自体が別物になっているため変化しません。コピー後のリストのみ内容が更新されます。
list_value = ['猫', '犬']
copied_list_value = list_value.copy()
copied_list_value[0] = '兎'
print('元のリスト:', list_value)
print('コピー後のリスト:', copied_list_value)
コード実行結果の出力内容:
元のリスト: ['猫', '犬']
コピー後のリスト: ['兎', '犬']
一方でイコールの記号を使ってcopyメソッドの記述を省略して別の変数名のリストを割り振った場合には、名前(呼び方)が違うだけで参照されているのは同じ箱なので、コピー後のリストの変数の値を更新するとコピー前のリストの値も同時に変更されてしまいます。
list_value = ['猫', '犬']
copied_list_value = list_value
copied_list_value[0] = '兎'
print('元のリスト:', list_value)
print('コピー後のリスト:', copied_list_value)
コード実行結果の出力内容:
元のリスト: ['兎', '犬']
コピー後のリスト: ['兎', '犬']
追記 : 以下の記述でシャローコピーの言葉の説明が正しくないとコメントで @shiracamus さんからご指摘いただきました。詳しくはコメントの方をご確認ください。
このようなそれぞれのコピーの仕方をシャローコピー(shallow copy)とディープコピー(deep copy)とそれぞれ呼びます。
シャローコピーは名前だけ変える(copyメソッドを経由していないケース)のものが該当します。シャロー(shallow)は「浅い」といった意味がある通り、箱の中身まではコピーしません。
一方のディープコピーはcopyメソッドなどを経由するコピーを仕方が該当し、ディープ(deep)の「深い」という意味の通りに箱の中身までコピーするような挙動になります。
シャローコピーとディープコピーのこの違いはプログラムを書く際にうっかりミスしてしまうことがあります。例えば「元の値も更新されている想定だったのに更新されていない」といったケースや、逆に「予期せぬタイミングで元の値に変更が入ってしまっていた」といったケースが該当します。
何故全てディープコピーでは無いのか?紛らわしくないか?という点ですが、紛らわしいのは確かで、且つミスも誘発しがちです。ただし毎回ディープコピーが実行されるとリストなどの内容のサイズによってはパソコンなどに大きな負担になってしまいます。
たとえばリストなどは数百万・数千万といった数の値を格納することができます。これが頻繁にディープコピーが実行されるととても負荷がかかってしまいます。そのため通常はシャローコピーで扱われ、ディープコピーが必要なケースでのみ明示的にcopyメソッドなどを呼び出してディープコピーをする・・・といった形になっています。
シャローコピーとディープコピーは概念としては理解していて、且つ長くプログラミングを続けていても稀にうっかりミスしてしまったりすることがあるので気を付けていきましょう。
リスト内での要素の出現回数を調べる : countメソッド
countメソッドはリスト内に含まれている特定の値の件数を算出します。第一引数に対象の値を指定します。
例えば第一引数に'猫'と指定すれば、リスト内の'猫'という値の件数が返却されます。
list_value = ['猫', '犬', '猫', '猫', '犬']
print(list_value.count('猫'))
コード実行結果の出力内容:
3
対象の要素がどのインデックスに存在するかを調べる : indexメソッド
リスト内で特定の要素が何番目のインデックスに存在するかを調べるにはindexメソッドを使います。
第一引数に検索したい要素を指定して、返却値に結果のインデックスが設定されます。
インデックスの値は他と同様0からスタートするので、先頭であれば0のインデックス、2番目であれば1のインデックス、3番目であれば2のインデックスといったように設定されます。
list_value = ['犬', '兎', '猫', '犬']
print('猫という値の存在するインデックス:', list_value.index('猫'))
コード実行結果の出力内容:
猫という値の存在するインデックス: 2
複数の対象の値が存在する場合には先頭のインデックスが返却されます。
list_value = ['犬', '兎', '猫', '犬', '猫']
print('猫という値の存在するインデックス:', list_value.index('猫'))
コード実行結果の出力内容:
猫という値の存在するインデックス: 2
リスト内に含まれない値を引数に指定した場合にはエラーとなります。
ValueError: '猫' is not in list
「'猫'という値がリストの中に含まれていませんよ」といったようなエラーメッセージになります。