見出し画像

3月21日 日曜日 0時 | Python ループの中のtry/except

Pythonなどのプログラミングを勉強中だと聞いている友人の投稿(勉強記録のような内容)を見かけて、その中のサンプルコードを読んでいたら、ふと気になったところがあった。

forループの中でtry/except文を使っているこんなコードを見かけたのだ。

# soupはBeautifulSoupが
# とあるWEBサイトを取り込んだオブジェクト
# find_allメソッドでマッチする要素を
# イテレートできる
for anchor in soup.find_all('a'):
   try:
       # href属性の取得に成功
       print(anchor['href'])
   except KeyError:
       # href属性の取得に失敗
       print("Error: No href")
     

もし soup.find_all('a') によってアンカー要素が10個見つかったとして、途中のどこか(例えば3個めなど)に

<a>リンク</a>

このようなクリックしても無反応なダミーのリンクが含まれていたらどうなるのだろうか?

このようなダミーリンクは、HTMLの世界ではまったく正当な表現であり、エラーが吐かれたりブラウザから拒否されるものではない。つまり、こういったダミーリンクがHTMLの中に含まれていないという保証はどこにもない。

このケースに関して答えを言ってしまうと、href の取得に失敗した時点で for ループは break され、それ以降のアンカー要素は単に無視される。

問題は、これが(A)「望む動作」ならばそれでOKだし、もし(B)「望まない動作」ならば修正が必要だということだ。
ここに尽きる。

それはこのスクリプトの要件によって決まる。

(A)対象HTML内に含まれるアンカー要素は全てhref属性を持っているはずなので、それを確かめたい。もしhref属性を持っていない要素がひとつでもあれば、その時点でエラーを raise してスクリプトの処理を中断したい。
→ この要件ならばコードはこのままで良い。望む動作を実現している。

(B)そうではなく、対象HTML内に含まれるアンカー要素は全てhref属性を持っているとは限らないという前提に立っており、href属性を持っていればその内容を知りたいが、持っていないものについては特に関心がない(スルーしたい)。可能な限り包括的にアンカー要素とそのhref属性を確認したい。
→ このような要件であれば、コードは望む動作を実現できていないので修正する必要がある。

もし(B)の要件に沿ってコードを修正したいと思ったらどう修正したら良いだろうか?

多分、最小限のコード変更でことを済ませようと思うなら、except 節の最後に continue を書いてしまえば良い。

for anchor in soup.find_all('a'):
   try:
       # href属性の取得に成功
       print(anchor['href'])
   except KeyError:
       # href属性の取得に失敗
       print("Error: No href")
       # ループを続行
       continue

これだけで動作的には(B)の要件をクリアする。

# ーーー ここからは文化や価値観の問題 ーーー #
しかし、である。

anchor に 'href' キーが入っているか否かを確認したいだけ、かつ、なかったからと言ってどうということはない、という状況で try/except 構文はちょっと大げさな気がする、個人的には。except に明示的に KeyError が指定されているので意図は十分わかるのだが、もっとシンプルにできないものか。

というわけでこんなコードに改めてみるとどうだろう?

for anchor in soup.find_all('a'):
   if 'href' in anchor:
       # href属性の取得に成功
       print(anchor['href'])
   else:
       # href属性の取得に失敗
       print("Error: No href")

単なる if 文で「anchor に 'href' キーが入っているか否かを確認」している。
if 文でループのたびに確認が走るだけなので、continue などの処理も不要である。

このようにプログラムに対して考えが及ぶ範囲で動作の可能性を考え、判断の基準をそもそもの要件に立ち返ることで再確認してみることが地味に大事だと気付かされたコードであった。(ありがとう、Tさん。)

SN​

この記事が気に入ったらサポートをしてみませんか?