・except requests.exceptions.ConnectionError as e:を書かないでインターネットから切り離す →相手のWebサーバーに接続できないことから異常終了となり、コンソールに赤字で異常終了の結果が表示されます。また、adapters.pyというモジュールがでてきました。
・except requests.exceptions.ConnectionError as e:を書くと、異常終了せずにコンソールに以下の結果が出力されました。
HTTPSConnectionPool(host='flask.pc5bai.com', port=443): Max retries exceeded with url: /stock/info/csv/ (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x0000025FD02736D0>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))
藤本 博子さんの投稿
(投稿ID: 5420)
sample32.pyの28行目、動画31行目
def get_current_unit_price(self):
最後のretrne Noneは不要だと思いました。
小川 慶一さんのコメント
(コメントID: 7967)
> 最後のretrne Noneは不要だと思いました。
あってもなくても良いです。
明示的に None を返しているのは、「for 文内での return 文で処理が終わらない可能性もある」ということを明示するためです。
というかそもそも、ここと response.status_code が 200 ではない場合のところでは、何らかの例外を発生させるほうがよいです。
以下は一例ですが、こうして return 文 を raise 文で置き換えてみると、「最終行に何かあったほうが良い」ということが理解できるかと思います。
なお、この実装で raise 文ではなく return 文にしたのは、raise 文は次章まで講座で正式には紹介されていなかったからです。(ほとんどの受講生の方は、この動画あたりでは、クラスとインスタンスのことだけですでに脳みそがいっぱいかな、と思いまして...)
まずは、以上のとおりです。
藤本 博子さんのコメント
(コメントID: 7968)
ご回答くださいまて、ありがとうございます。
>明示的に None を返しているのは、「for 文内での return 文で処理が終わらない可能性もある」ということを明示するためです。
というかそもそも、ここと response.status_code が 200 ではない場合のところでは、何らかの例外を発生させるほうがよいです。
安直にresponse.status_codeが200以外だったら例外処理をする、という条件分岐があればよいと思ってました。
「for 文内での return 文で処理が終わらない可能性もある」場合について、念頭にいれておりませんでした。
堅牢なプログラムを構築する上で、実行時に発生するエラーをキャッチする、制御する仕組みを十分すぎるくらい考慮することが、大切なのだとあらためて思いました。
安易に考えていたので、大変勉強になりました。
ありがとうございます。
>この動画あたりでは、クラスとインスタンスのことだけですでに脳みそがいっぱいかな、と思いまして...)
はい、私の脳みそはいっぱいいっぱいです。(;^_^A
小川 慶一さんのコメント
(コメントID: 7969)
以下の sample1.py, sample2.py を比較してみましょう。
どちらも、呼び出しの流れは以下のとおりです。
[0] 大元の呼び出し (if __name__ == '__main__': 内)
↓
[1] get_price_string #内部で呼び出した関数の戻り値(JPYでの総額、数値)を文字列になおす
↓
[2] get_price_per_yen #内部で呼び出した関数の戻り値(USDでの総額、数値)を日本円になおす
↓
[3] get_stock_price #USDでの単価を元にして、総額を取得
↓
[4] Stock クラスのメソッド get_current_unit_price #USDでの単価を取得
違いは、Stock クラスのインスタンスメソッドで異常系の処理が起きたときの処理です。
sample1.py では、ValueError を返します。
sample2.py では、戻り値として None を返します。
sample1.py では、[4] で異常系の処理に進む場合の処理を書くのは簡単です。
[0] の大元での呼び出しで、 try ... except 文を加えておけばOK。
「こうすれば、穏やかでない処理結果になったとき、[1], [2], [3] でどうするか」といったことは一切考えずに済みます。
sample1.py
sample2 では、そうはいきません。
[4] が None を返すので、 [3] は「None を受け取ったらどうする」といった条件分岐を必要とします。
[3] が返す値がおかしな場合に備えて、[2]でも条件分岐を必要とします。
[2] が返す値がおかしな場合に備えて、[1]でも条件分岐を必要とします。
...
ということで、異常終了しないで済むプログラムを書こうとするならば、以下のように煩雑なものになってしまいます。
しかも、さんざん煩雑な処理を書いたわりには、異常終了したときの情報として出力できるのは、「価格の取得に失敗しました」という文字列1パターンだけです。
sample1 では、status_code で異常検出した場合、csvを解析して所望のキーが見つからなかった場合で出力内容を変更できました。それに比べると、出力内容も残念です。
sample2.py
ということで、冒頭に記載した結論に行き着きます。
例外で処理してしまうことのメリットは、深いところで予定外のことが起きてしまったときのための処理を簡単に書けることです。
小川 慶一さんのコメント
(コメントID: 7970)
以下の sample3.py では、 ValueError だけでなく、 requests.exceptions.ConnectionError もハンドリングするようにしました。
そのためにしたことは、sample1.py のプログラムで、同レベルの処理としてほんの2行を追加しただけです。
それですべてが済むのですから、簡単ですね。
さきほどのsample2のようなコードだと、 requests.exceptions.ConnectionError が生じた場合のハンドリングをどこでどう記述するか、かなり悩みそうです。(というか、要するに、設計が悪いです)
端末のwifiをオフにする等、手元のマシンをインターネットから切り離しから実行してみてください。
sample3.py
藤本 博子さんのコメント
(コメントID: 7971)
クラスの講座の後に例外処理を勉強することになるのですが、この段階で勉強することができて、ありがたいと思いました。ありがとうございます。
通常、実行時にエラーが発生すると異常終了となってしまいます。
raise文は、問題が発生したときに、呼び出し元にメッセージとともに発生するエラー(例外)を返すことができるのですね。
例外処理を行うことにより、実行時エラーを検出した場合、異常終了せずにその出力内容を変更することができることを、サンプルコードで学ぶことができました。
>sample1.py で、status_code で異常検出した場合と、csvを解析して所望のキーが見つからなかった場合で出力内容を変更できました。
実際に、コードを動かして検証しました。
・sample1.pyは、URLが間違っていた場合は、「サーバからの応答不正:404」という結果をコンソールに表示させて終了することができます。
・csvを解析して所望のキーが見つからなかった場合は、所望のキーの情報が見つからなかったことをコンソールに表示させて終了します。
・csvを解析して所望のキーと一致した場合
try文の中で実行したコードでエラーがなく、exceptをぬけた場合は、elseで戻り値をプリントする処理ができるのですね。
sample2.py で、戻り値として Noneを返すことを想定し、逐一None(おかしな戻り値)が返される場合に備えた条件分岐を書くと、その条件分岐を書く手間や煩雑さに加え、情報が多くなり可読性が悪くなる・見通しずらくなること改めてコードを動かしてみて実感しました。
>try ... except 文を使うことのメリットとして、内部で呼び出されたプログラム内の様々なタイミングで生じかねない種々のエラーを一箇所でハンドリングできるということも挙げられます。
>以下の sample3.py では、 ValueError だけでなく、 requests.exceptions.ConnectionError もハンドリングするようにしました。
そのためにしたことは、sample1.py のプログラムで、同レベルの処理としてほんの2行を追加しただけです。
それですべてが済むのですから、簡単ですね。
端末のwifiをオフにする等、手元のマシンをインターネットから切り離しから実行してみてください。
requestsを利用する際のエラーハンドリングについてもご教示くださいまして、ありがとうございます。
・except requests.exceptions.ConnectionError as e:を書かないでインターネットから切り離す
→相手のWebサーバーに接続できないことから異常終了となり、コンソールに赤字で異常終了の結果が表示されます。また、adapters.pyというモジュールがでてきました。
・except requests.exceptions.ConnectionError as e:を書くと、異常終了せずにコンソールに以下の結果が出力されました。
これから自分も例外処理を踏まえたコードがかけるよう、勉強をすすめていきたいです。
ありがとうございます。
小川 慶一さんのコメント
(コメントID: 7972)
ここまで実装とその機能について説明したうえで改めてシンプルに言うと、以下のとおりです。
[a] 実行時エラーにも、想定内のもの、想定外のものがある。
[b] 想定内のものは、 try ... except 節でハンドリングする。(想定外の場合は潔く強制終了を受け入れる)
ということで。
「出力内容を変更する」ことも可能ですが、そのためだけにこの構文があるわけではないということは改めてお伝えしておきます...。
たとえば、今回提示したサンプルコードでも、 requests.exceptions.ConnectionError が出た場合には「別サーバにデータを取りに行く」とか、「保存している中で最新のCSVからデータを取ってきて出力する」とか、そういう処理に進むかもしれません。
> try文の中で実行したコードでエラーがなく、exceptをぬけた場合は、elseで戻り値をプリントする処理ができるのですね。
finally というキーワードもあります。
これも含めて、次章で説明しています。
> 逐一None(おかしな戻り値)が返される場合に備えた条件分岐を書くと
例外が発生した場合のことを考慮外にすることで、これらの関数の定義もシンプルにできますね。
Excel VBAにはない概念や構文の形も含まれているので最初はとっつきにくいかと思います。
(そもそも、「クラスとインスタンス」もそうですが... (^^; )
ですが、仕様の意図を汲んで実装できるだけの力がついてきたときには、Excel VBAとはまったく違う楽しさを実感できるだろうとも思います。
ひきつづき、楽しんで学んでください v(^^*
藤本 博子さんのコメント
(コメントID: 7973)
丁寧に解説くださいまして、ありがとうございます。
これからエラーのハンドリングについても勉強して、適宜必要に応じた処理ができるようになりたいです。
ありがとうございます。
>ですが、仕様の意図を汲んで実装できるだけの力がついてきたときには、Excel VBAとはまったく違う楽しさを実感できるだろうとも思います。
楽しんで学んで、実感できるようになりたいです。