class ValueError(Exception):
""" Inappropriate argument value (of correct type). """
def __init__(self, *args, **kwargs): # real signature unknown
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
value = 200
class VE(ValueError):
def __init__(self, *args: object) -> None:
super().__init__(*args)
print(*args)
if value > 100:
v = VE('value の値は 100 以下にしてください。', 'hoge hoge')
raise v
print("raise文の後の処理は実行されません。")
伊久間博之さんの投稿
(投稿ID: 5418)
まずはメモ。
これでなんで
と表示できるのか知りたかったので、Ctrl+BでValueErrorを掘ってみました。
しか定義されてなく、なぜこれで標準出力に表示されるのだ??? としばし考え込んでしまいました
が、readme.md
> 実行時エラーを発生させるのは、 raise 文。
と書いてあることに思い至り、
1. ValueErrorクラスに文字列を渡しインスタンス生成
2. そのインスタンスをraiseに渡す
3. raiseの中で渡されたValueErrorインスタンスが保持している文字列を標準出力に出力
という処理が行われているんだろう、と推測しました。
検証のため、以下のようなコードを書いてみました。
①
Ctrl+Bを使ってもraiseを掘り下げることはできなかったのでraiseの中で何が起きているかはわかりませんが、期待した結果になりました。
以下質問です。
①の検証コードを「実行」したときと「デバッグで実行」したときにコンソールに出力される結果が変わるのはなぜでしょうか。
実用上問題なさそうなのでスルーしてもよさそうなものですが、ちょっと気になりまして。
「実行」したときのコンソール出力
「デバッグで実行」したとき
デバッガが内部で何やら処理してコンソールに見やすく表示してくれているということでしょうか?
小川 慶一さんのコメント
(コメントID: 7950)
順調ですねw
理由を書くと、 import 不要で使えるビルトインの機能の多くは、CPython等の実装のほうで具体的な処理が記述されているからです。(*)
ValueError もその類ではないかと。
> ①の検証コードを「実行」したときと「デバッグで実行」したときにコンソールに出力される結果が変わるのはなぜでしょうか。
> 実用上問題なさそうなのでスルーしてもよさそうなものですが、ちょっと気になりまして。
ご想像のとおり、Pycharmのスクリプトがフックしているからです。
試しに、前者、後者それぞれ、引数を縦に書いてみます。
だいぶ見通しが良くなり、それぞれの python コマンドがやりたいことがどんな引数を受け取っているか、明確になるかと。
「実行」
「デバッグで実行」
「デバッグで実行」のときに指定されいてるオプションは、第一引数で指定された pydevd.py (Pycharm が提供している)が受け取っているようですね。
pydev.py のソースを読んでみてください。
以降の引数がこのファイル内で処理されている部分を見つけられます。
(*)
以下、伊久間さんもご想像のことと思いますが...。
どんな言語で書かれたプログラムも、実行されるときは、そのOSで解釈できなければなりません。
ということで、どこかから先はC言語等で書かれているわけです。
「では、実際の処理のどこまではCPython等の実装で、どこからはPythonで書くということにしようか」というのは言語の仕様を設計者側で決めている問題です。
("python 実装 ソースコード cpython" 等のキーワードで検索すると 「CPython で書かれたソースを見つけて読んでみよう」という趣旨のブログ記事等見つけられます)
ValueError は Exception を継承しています。なので、「Exception は C で、ValueErrorは Python でソースコードが書かれていても別に良いじゃないか」という考え方もできないことはないです。
ですが、頻繁に利用するものなので、高速化のために ValueError も C で書かれているのでしょう。
ValueErrorくらいだとちょっとピンとこないかもしれませんが、たとえば、int型、 float型、 bool型等も CPython で実装されています。
「Number型だけCPythonで定義しておけば、int型、 float型、 bool型も継承でなんとかなるのでは?」という考え方もできます。
でも、頻用する型なので、「Number型だけCPythonで、あとはPythonで」というのは、パフォーマンスへの影響が大きそうですね。
[参考] 3.データモデル - 3.2. 標準型の階層
https://docs.python.org/ja/3/reference/datamodel.html#the-standard-type-hierarchy
ValueError も同様です。
基本機能の解説用に本講座で紹介しているコード程度ですと ValueError すら滅多にでてきませんし、出てきたら「これは悪い例です」ということで紹介され、そこでコードは強制終了で終わりということがほとんどです。
ですが、中規模のライブラリになってくると、 try ... except 節で ValueError やこれを継承した例外をハンドリングするような処理はしょっちゅうでてきます。
出現頻度を考えると、こんな実装の分担になっていることについては、僕としては納得です。
ビルトイン関数/クラスについては、やはり、公式ドキュメントを読むのかいちばんと思います。
8. エラーと例外
https://docs.python.org/ja/3/tutorial/errors.html
伊久間博之さんのコメント
(コメントID: 7953)
フックという言葉がわからなかったので、調べてみました。
「プログラム中の特定の箇所に、利用者が独自の処理を追加できるようにする仕組みである。また、フックを利用して独自の処理を追加することを「フックする」という。」
「デバッグのための情報収集にも有効である。」
(wikipedia)
pydev.pyに処理を渡し、いくつかの引数を渡しているわけですね。
pydev.pyのソースコードを見つけました。
https://github.com/fabioz/PyDev.Debugger/blob/main/pydevd.py
import argparse の存在を期待していたのですが、どうやらなさそう(?)
mainメソッドの中を流し読みしてみましたがなんだかよくわからずソーッと閉じました😅
ともあれ、「実行」時はpython.exe、「デバッグで実行」時にはフックしてpydev.pyが処理を担っているためコンソール表示内容が変わるというわけですね。
どこからはCPythonの実装なのかというお話、大枠は理解いたしました。
(提示して頂いたドキュメントを読みこなすには至っていませんが)
cpythonのexeptionもgithubで見てみましたが、ジェット機からジャングルを見ているような感じですw
https://github.com/python/cpython/blob/main/Objects/exceptions.c
いずれは自分の足でジャングルの表層くらいには入っていけるよう、技術と体力を身につけたいと思います。
伊久間博之さんのコメント
(コメントID: 7954)
勝手に同じだろうと思い込んでいました、、、
readme.md
【重要】 Traceback は、必ず読むこと!
というのがありましたができていませんでした反省 😅
小川 慶一さんのコメント
(コメントID: 7956)
CPythonでの実装のこともさっぱりです。それでもPythonで仕事をする分には支障ありませんので、そこはご心配なく (^^;
>【重要】 Traceback は、必ず読むこと!
>というのがありましたができていませんでした反省 😅
何を習得するときも、最初はどうしても視野が狭くなってしまうものです。
視野はだんだん広くなっていきます。また、「何に気づき得るか」というところの質が変わってきます。
伊久間博之さんのコメント
(コメントID: 7961)
>CPythonでの実装のこともさっぱりです。それでもPythonで仕事をする分には支障ありませんので、そこはご心配なく (^^;
安心しましたw
>何を習得するときも、最初はどうしても視野が狭くなってしまうものです。
>視野はだんだん広くなっていきます。また、「何に気づき得るか」というところの質が変わってきます。
運転免許取り立ての初心者が運転することにいっぱいいっぱいで周りの景色を楽しめない、みたいな感じですかね。
今はとにかく量稽古してPythonの感覚を体得したいと思います。
小川 慶一さんのコメント
(コメントID: 7962)
ということで全体の流れは漠然と掴めます。
ですが、僕もそのくらいです。
OSとPythonコマンドの関係とか、デバッガがフックする仕組みとか、内部で呼ばれているPyDevとか、そういうものについての知識がないので、内部の記述についてはさっぱりです。
> 運転免許取り立ての初心者が運転することにいっぱいいっぱいで周りの景色を楽しめない、みたいな感じですかね。
ですね。
「問題が生じていても問題だと分からない」とか、「危険が迫っていても、気づけない」とか。
> 今はとにかく量稽古してPythonの感覚を体得したいと思います。
「どうせ読めない」と分かっていても、いちおう開いて読もうとしてみるというあり方がとても大切です。
上手な人のコードを眺めるだけでも、「複数の部品を置くとして、こういう順番で置くものなんだな」とか、そういう感覚的なところに少しは響きますので。
あと、「しばらくぶりに読んでみたら、結構読めたぞ」という体験も、もっと前に「ぜんぜん分からなかった」という経験があってこそのものです。
大きなプログラムを見たあとに本講座程度のサンプルコードを読むと感覚もまた違うかと思います。
以前は頭がパンクしそうになりつつ読んでいたソースコードも、まとまりのよい小品に見えたりするだろうとも思います。(2000行以上のコード→数十行レベルですからね (^^; )
伊久間博之さんのコメント
(コメントID: 7966)
なるほど。そのようなあり方を目指して実践していきます。
上手い人がどうやって書いているかなどを漆塗りのように少しずつでも取り込みたいと思いす。