Pythonインタラクティブガイド - ステップ1 基本的な文法とデータ型 (6) - リスト (2)
シリーズ - Pythonインタラクティブガイド
コンテンツ
Info
- 本講座「Pythonインタラクティブガイド」は、手を動かしながらPythonプログラミングの基礎を学べる実践的な入門講座です。
- 「スタイルガイド」では、Pythonで読みやすくきれいなコードを書くためのガイドライン(PEP8)を主に紹介しています。
- 各コード例はその場で実行して結果を確認できます。
ページ再読み込みで元に戻るので、自由に試してみてください。
「ステップ1 基本的な文法とデータ型」の続きです。
1.5. データをまとめる
1.5.1. リスト
リストの操作
存在チェック(in, not in)
- ある要素がリスト内に存在するかどうかを確認するためには、
in演算子を使用します。
not in演算子を使用すると、in演算子と逆の判定を行います。
スライス
[開始位置:終了位置]のように範囲を指定して、リストの一部分を取得することができます。- このようにして取得できるリストの一部分のことを スライス といいます。
- この場合、
開始位置以上終了位置未満 (開始位置 <= x < 終了位置)の要素からなるスライスとなり、
終了位置で指定したインデックスの要素は 含まれない ことに注意してください。
- 指定した範囲が実際のリストの範囲を超えている場合、
エラーは発生せずに、指定範囲のうち実際の範囲内のスライスを返します。- 指定した範囲に値が1つも存在しない場合、空のリスト(
[])を返します。 - スライスの場合、範囲外のインデックスを指定しても
IndexErrorは発生しません。
- 指定した範囲に値が1つも存在しない場合、空のリスト(
- 「開始位置」を省略した場合(
[:終了位置])、先頭からのスライスとなります。 - 「終了位置」を省略した場合(
[開始位置:])、末尾までのスライスとなります。
[開始位置:終了位置:ステップ数]のように指定すると、ステップ数で指定した個数飛びのスライスを取得します。
- 「ステップ数」にマイナスの数を指定すると、逆順のスライスを取得できます。
- 後ろから逆順に進むため、
開始位置以下終了位置超 (開始位置 >= x > 終了位置)
の範囲のスライスとなります。終了位置の要素はスライスに含みません。
- 後ろから逆順に進むため、
- マイナスのステップ数の場合、
開始位置を空にすると、末尾からのスライスとなります。終了位置を空にすると、先頭までのスライスとなります。
📚練習問題
1~20を要素とするリストを使用して、以下を実施しましょう。
- 5~15の数からなるスライスを出力しましょう。
- 5の倍数のスライスを出力しましょう。
- 3の倍数の逆順のスライスを出力しましょう。
スライスによる書き換え
- スライスを使用してリストの要素を書き換えることができます。
- サイズの異なるリストの代入もエラーが発生せずできてしまうため、注意してください。
リストのコピー
「1.4. 変数と代入」にて、変数は「ラベル」のようなものであると説明しました。
ここで、Pythonでよくある間違いを紹介して、変数が「ラベル」であることを示します。
上記コードを実行すると分かる通り、元の価格リスト(original_prices)も変化してしまっています。
この理由は、変数が「ラベル」であることを意識すると理解できます。
graph TD
subgraph "ステップ3: 要素の変更"
list3["[500, 1500, 5000]"]
original_prices3[original_prices] --> list3
new_prices3[new_prices] --> list3
end
subgraph "ステップ2: 新しい変数に代入"
list2["[1000, 1500, 5000]"]
original_prices2[original_prices] --> list2
new_prices2[new_prices] --> list2
end
subgraph "ステップ1: 初期状態"
list1["[1000, 1500, 5000]"]
original_prices1[original_prices] --> list1
end
style list1 fill:#fff3bf,stroke:#333,stroke-width:2px
style list2 fill:#fff3bf,stroke:#333,stroke-width:2px
style list3 fill:#fff3bf,stroke:#ff0000,stroke-width:2px
このように、Pythonでは変数を新しい変数に代入した場合、
新しい変数にデータが移るのではなく、新しい変数も元のデータを指し示すラベルとなります。
(つまり、最初の変数も新しい変数もどちらも同じ元データを参照します。)
- リストをコピーするには、
copy()メソッドを使用します。
graph TB
%% ステップ1: 初期状態
subgraph step1["ステップ1: 初期状態"]
direction TB
op1[original_prices] --> list1["[1000, 1500, 5000]"]
end
step1 --> step2
%% ステップ2: copy()メソッド実行後
subgraph step2["ステップ2: copy()メソッドを使ってリストをコピー"]
direction TB
op2[original_prices] --> list2["[1000, 1500, 5000]"]
list2 -. "copy()" .-> list2_copy["[1000, 1500, 5000] (コピー)"]
np2[new_prices] --> list2_copy
end
step2 --> step3
%% ステップ3: 変更後
subgraph step3["ステップ3: 新しいリスト側の要素を変更後"]
direction TB
op3[original_prices] --> list3["[1000, 1500, 5000]"]
np3[new_prices] --> list3_copy["[500, 1500, 5000]"]
end
%% スタイル設定
style list1 fill:#fff3bf,stroke:#333,stroke-width:2px
style list2 fill:#fff3bf,stroke:#333,stroke-width:2px
style list3 fill:#fff3bf,stroke:#333,stroke-width:2px
style list2_copy fill:#fff3bf,stroke:#333,stroke-width:2px
style list3_copy fill:#fff3bf,stroke:#ff0000,stroke-width:2px
linkStyle 3 stroke:#4285F4,stroke-width:2px
- 開始位置と終了位置を省略したスライス
[:]でも同様にコピーすることができます。
しかし、copy() メソッドやスライス [:] でも問題が生じる場合があります。
以下でその例を紹介します:
これは、copy()メソッドやスライス[:]では、外側のリスト([..., ..., ...])は複製されても
内側の値(["apple", 120]など)は複製されず、元のリストと同じ値を参照してしまうためです。
このようなコピーのことを「浅いコピー (シャローコピー)」といいます。
graph TB
%% ステップ1: 初期状態
subgraph step1["ステップ1: 初期状態"]
direction TB
np1[name_and_prices] --> outer1["[..., ..., ...]"]
outer1 --> apple1["['apple', 120]"]
outer1 --> orange1["['orange', 150]"]
outer1 --> grape1["['grape', 180]"]
end
step1 --> step2
%% ステップ2: copy()メソッド実行後
subgraph step2["ステップ2: copy()メソッドを使ってリストをコピー"]
direction TB
np2[name_and_prices] --> outer2["[..., ..., ...]"]
nnp2[new_name_and_prices] --> outer2_copy["[..., ..., ...] (コピー)"]
outer2 --> apple2["['apple', 120]"]
outer2 --> orange2["['orange', 150]"]
outer2 --> grape2["['grape', 180]"]
outer2_copy --> apple2
outer2_copy --> orange2
outer2_copy --> grape2
end
step2 --> step3
%% ステップ3: 変更後
subgraph step3["ステップ3: 新しいリスト内の要素(リスト)の要素を変更後"]
direction TB
np3[name_and_prices] --> outer3["[..., ..., ...]"]
nnp3[new_name_and_prices] --> outer3_copy["[..., ..., ...] (コピー)"]
outer3 --> apple3["['apple', 120]"]
outer3 --> orange3["['orange', 200]"]
outer3 --> grape3["['grape', 180]"]
outer3_copy --> apple3
outer3_copy --> orange3
outer3_copy --> grape3
end
%% スタイル設定
style outer1 fill:#fff3bf,stroke:#333,stroke-width:2px
style outer2 fill:#fff3bf,stroke:#333,stroke-width:2px
style outer3 fill:#fff3bf,stroke:#333,stroke-width:2px
style outer2_copy fill:#fff3bf,stroke:#333,stroke-width:2px
style outer3_copy fill:#fff3bf,stroke:#333,stroke-width:2px
style apple1 fill:#ffc078,stroke:#333,stroke-width:2px
style orange1 fill:#ffc078,stroke:#333,stroke-width:2px
style grape1 fill:#ffc078,stroke:#333,stroke-width:2px
style apple2 fill:#ffc078,stroke:#333,stroke-width:2px
style orange2 fill:#ffc078,stroke:#333,stroke-width:2px
style grape2 fill:#ffc078,stroke:#333,stroke-width:2px
style apple3 fill:#ffc078,stroke:#333,stroke-width:2px
style orange3 fill:#ffc078,stroke:#ff0000,stroke-width:2px
style grape3 fill:#ffc078,stroke:#333,stroke-width:2px
- 要素(リスト)内の要素を変更するのではなく、要素自体を(丸ごと)入れ替える場合は、
「1.4.3. 変数への再代入」で説明した「ラベルの付け替え」操作となるため、元のリストには影響を与えません。
graph TB
%% ステップ1: 初期状態
subgraph step1["ステップ1: 初期状態"]
direction TB
np1[name_and_prices] --> outer1["[..., ..., ...]"]
outer1 --> apple1["['apple', 120]"]
outer1 --> orange1["['orange', 150]"]
outer1 --> grape1["['grape', 180]"]
end
step1 --> step2
%% ステップ2: copy()メソッド実行後
subgraph step2["ステップ2: copy()メソッドを使ってリストをコピー"]
direction TB
np2[name_and_prices] --> outer2["[..., ..., ...]"]
nnp2[new_name_and_prices] --> outer2_copy["[..., ..., ...] (コピー)"]
outer2 --> apple2["['apple', 120]"]
outer2 --> orange2["['orange', 150]"]
outer2 --> grape2["['grape', 180]"]
outer2_copy --> apple2
outer2_copy --> orange2
outer2_copy --> grape2
end
step2 --> step3
%% ステップ3: 要素丸ごと入れ替え後
subgraph step3["ステップ3: 新しいリストの1要素を丸ごと入れ替え後"]
direction TB
np3[name_and_prices] --> outer3["[..., ..., ...]"]
nnp3[new_name_and_prices] --> outer3_copy["[..., ..., ...] (コピー)"]
outer3 --> apple3["['apple', 120]"]
outer3 --> orange3["['orange', 150]"]
outer3 --> grape3["['grape', 180]"]
outer3_copy --> apple3
outer3_copy --> orange3
outer3_copy -. "置き換え前" .-> grape3
outer3_copy -- "置き換え後" --> banana3["['banana', 200]"]
end
%% スタイル設定
style outer1 fill:#fff3bf,stroke:#333,stroke-width:2px
style outer2 fill:#fff3bf,stroke:#333,stroke-width:2px
style outer3 fill:#fff3bf,stroke:#333,stroke-width:2px
style outer2_copy fill:#fff3bf,stroke:#333,stroke-width:2px
style outer3_copy fill:#fff3bf,stroke:#333,stroke-width:2px
style apple1 fill:#ffc078,stroke:#333,stroke-width:2px
style orange1 fill:#ffc078,stroke:#333,stroke-width:2px
style grape1 fill:#ffc078,stroke:#333,stroke-width:2px
style apple2 fill:#ffc078,stroke:#333,stroke-width:2px
style orange2 fill:#ffc078,stroke:#333,stroke-width:2px
style grape2 fill:#ffc078,stroke:#333,stroke-width:2px
style apple3 fill:#ffc078,stroke:#333,stroke-width:2px
style orange3 fill:#ffc078,stroke:#333,stroke-width:2px
style grape3 fill:#ffc078,stroke:#333,stroke-width:2px
style banana3 fill:#ffc078,stroke:#ff0000,stroke-width:2px
📚練習問題
上記コードにて、copy()メソッドでコピーせずに実施した場合にどうなるか考えてみましょう。
浅いコピーと深いコピー
上記の浅いコピー(シャローコピー)に対して、その要素や要素の要素などすべて含めて、
完全に複製するコピーのことを「深いコピー (ディープコピー)」といいます。
深いコピーは標準ライブラリ内のcopyモジュールのdeepcopy関数を用いて、以下のようにして実施できます:
(「モジュール」については、詳しくは後ほど解説します)
アンパック
- リストの要素をそれぞれ別の変数に代入したい場合、イコール(
=)の左側に変数をカンマ(,)区切りで書くことで代入できます。- この操作のことを アンパック (unpack) といいます。
- 変数の数と代入値の要素数が異なる場合、
ValueErrorが発生します。
- アンパック演算子
*を使うと、「最初の要素」と「その他」で分けるようなアンパックが可能です。
(掛け算で使われる2項演算子の*とは異なり、変数の頭に*をつけます。)
- 複数のアンパック演算子
*を使ったアンパックはSyntaxError(構文エラー)が発生します。
- アンパックで使わない値はアンダースコア (
_) で受け取ることができます。- これはその値を使わないということを明示するためのPythonでの慣習です。
bool関数による型変換
- リストに
bool関数を適用すると、空のリスト([])の場合にFalse、それ以外にTrueを返します。