こんにちは、バッヂーです。ひさしぶりのブログ更新です。
しばらく仕事が忙しかったり、オンライン英会話のレッスンをやってたり、趣味のプログラムを作ってたりなどなどで、すっかりブログがご無沙汰でした。
だいたい例年GW頃にやる気になって心を入れ替えるので、今年もブログ投稿にちょっとやる気が出ております。
まずはウォームアップがてら、仕事のプログラムを作成していて「あれ、これどうなるんだろう?」と思ったことを復習がてら実験してみたいと思います。
今回の実験はPythonのForループです
Forループの基本的動作は入門書や解説サイトにおまかせするとして、今回の実験はこれです。
- Pythonでリストのループ中に要素を追加するとどうなるか?
「え。今更!?」な感はしますね。一応図示するとこんな感じです。
結果もだいたい予想がつくのですが…どうでしょうか。
結果:フツーに動作します
そうですね。予想通りにフツーに動作します。一応まとめるとこんな感じです。
- ちゃんとリストに追加される。
- 追加されたところもループの一部として処理される。
- 読み出しているインデックス位置はリストの要素が増えても変わらない。
さっそく詳細を見ていきましょう。
ベースとなるプログラム
ベースとなるプログラムはこんな感じです。
apex_legends = [
"ブラッドハウンド",
"ジブラルタル",
"ライフライン",
"パスファインダー",
"レイス",
"バンガロール",
"コースティック",
"ミラージュ"
]
for index, legend in enumerate(apex_legends):
print("INDEX:%s LENGTH:%s NAME:%s" % (index, len(apex_legends), legend))
シンプルですね。リストの変数apex_legendsにAPEXのキャラクターを追加して、Forループで次々と表示させています。INDEXは読み出し位置のインデックス、LENGTHはapex_legendsの要素数、NAMEはキャラクターの名前です。
結果はこんな感じになります。
INDEX:0 LENGTH:8 NAME:ブラッドハウンド
INDEX:1 LENGTH:8 NAME:ジブラルタル
INDEX:2 LENGTH:8 NAME:ライフライン
INDEX:3 LENGTH:8 NAME:パスファインダー
INDEX:4 LENGTH:8 NAME:レイス
INDEX:5 LENGTH:8 NAME:バンガロール
INDEX:6 LENGTH:8 NAME:コースティック
INDEX:7 LENGTH:8 NAME:ミラージュ
これも疑問の余地がありませんね。
ループの途中でリストの末尾に要素を追加する
プログラムを次のように修正して、「読み出し位置のインデックスが3のとき、リストの末尾に2つ要素を追加する」ようにしてみます。
for index, legend in enumerate(apex_legends):
print("INDEX:%s LENGTH:%s NAME:%s" % (index, len(apex_legends), legend))
if index == 3:
apex_legends.append("オクタン")
apex_legends.append("ワットソン")
print(apex_legends)
最後にリストの中身を表示するようにしました。実行結果は次のとおりになります。
INDEX:0 LENGTH:8 NAME:ブラッドハウンド
INDEX:1 LENGTH:8 NAME:ジブラルタル
INDEX:2 LENGTH:8 NAME:ライフライン
INDEX:3 LENGTH:8 NAME:パスファインダー
INDEX:4 LENGTH:10 NAME:レイス
INDEX:5 LENGTH:10 NAME:バンガロール
INDEX:6 LENGTH:10 NAME:コースティック
INDEX:7 LENGTH:10 NAME:ミラージュ
INDEX:8 LENGTH:10 NAME:オクタン
INDEX:9 LENGTH:10 NAME:ワットソン
['ブラッドハウンド', 'ジブラルタル', 'ライフライン', 'パスファインダー', 'レイス', 'バンガロール', 'コースティック', 'ミラージュ', 'オクタン', 'ワットソン']
4行目のINDEX=3の時点で「オクタン」「ワットソン」が追加されるので、5行目の表示時にはリストの要素数が2つ増えています。追加された2つの要素はループの終わりに出現して、きちんと処理されていることがわかります。
ループの途中でリストの「途中」に要素を追加する
さらにプログラムを修正し、今度はリストの「途中」に要素を追加してみます。プログラムは次のようになります。
for index, legend in enumerate(apex_legends):
print("INDEX:%s LENGTH:%s NAME:%s" % (index, len(apex_legends), legend))
if index == 6:
apex_legends.insert(2, "オクタン")
apex_legends.insert(5, "ワットソン")
print(apex_legends)
「読み出し位置のインデックスが6のとき、リストのインデックス=2の位置に『オクタン』、リストのインデックス=5の位置に『ワットソン』を追加する」というプログラムですね。
すでに処理で通り過ぎた位置に要素を追加する、というところがポイントですね。結果は次のようになります。
INDEX:0 LENGTH:8 NAME:ブラッドハウンド
INDEX:1 LENGTH:8 NAME:ジブラルタル
INDEX:2 LENGTH:8 NAME:ライフライン
INDEX:3 LENGTH:8 NAME:パスファインダー
INDEX:4 LENGTH:8 NAME:レイス
INDEX:5 LENGTH:8 NAME:バンガロール
INDEX:6 LENGTH:8 NAME:コースティック
INDEX:7 LENGTH:10 NAME:バンガロール
INDEX:8 LENGTH:10 NAME:コースティック
INDEX:9 LENGTH:10 NAME:ミラージュ
['ブラッドハウンド', 'ジブラルタル', 'オクタン', 'ライフライン', 'パスファインダー', 'ワットソン', 'レイス', 'バンガロール', 'コースティック', 'ミラージュ']
8行目以降のNAMEが重複して出現しています。一つづつ追って確認していきましょう。
まずプログラムの4行目の処理でインデックス=2の位置に「オクタン」が追加されます。この時の読み出し位置のインデックスは6です(3行目)。
次にプログラムの5行目の処理でインデックス=5の位置に「ワットソン」が追加されます。この時の読み出し位置のインデックスは6です。
ここでのポイントは「読み出し位置のインデックスは6のまま」です。
次のループでは読み出し位置のインデックスは7に移り、前のループで要素が2つ増えたあとのリストのインデックス=7の要素「バンガロール」の表示をします。結果の8行目のところです。続いて次のループで「コースティック」の表示をします。結果の9行目のところです。
以上のように要素が増えることによって、読み出し位置のインデックス(これは変わらない)が示す要素の内容が変化します。なので「バンガロール」「コースティック」が2回登場することになります。
最終的にリストapex_legendsは結果の8行目のようになります。上の図と同じ順になっていることが確認できます。
実験のまとめ
実験からループの途中に要素を追加したときの挙動がわかりました。まとめると次のような感じでしょうか。
- ループの途中に末尾に要素を追加すると、追加した要素も処理に含まれる。
- ループの途中にリストの途中に要素を追加すると、リストは変化するが読み出しインデックスの位置は変わらない。
予想した通りの結果ですね。それでもこうやって確かめるておくことで、同じようなシーンに出会ったときに確信をもってすすめることができます。
ところでこの「リストのループ中に要素を追加する」の利用シーンって何でしょうか?
私が冒頭で「これどうなるんだろう?」と思ったシーンは次のようなスクレイピングのプログラムを作っているときでした。
当初、上の図の左のような構造を想定してスクレイピングのプログラムを作成しました。それぞれのページのURLをリストにしページごとに送出して情報を取得しようとしました。
ところが処理と検証を進めていると、次の図の左のような構造になっていることがわかりました。
それぞれのページの情報の取得の仕方は同じです。なのでページに新たなリンクを見つけたときは、情報を取得したうえで、送出URLリスト「scrape_urls」にURLを追加して、ループを拡張するようにしたのです。
上の「末尾に追加」は他にも利用シーンはありそうですね。
「途中に追加」はもちろん利用シーンはあるでしょうが、「ループ処理中に処理が終わった位置に追加」することについては利用シーンがまったく思いつきません。
私はPythonをほとんどスクレイピングにしか使っていませんので思いつかないのかもしれません。他の用途で使っている方には利用シーンがあるかも、です。また、もっとうまい方法があるのかもしれません。引き続き意識して、先人の書いたコードを眺めていきたいと思います。
ここではひとまず、私の理解できる範囲においてということで記事を終えたいと思います。ここまで読んでいただいてありがとうございました。