Ruleru
LLM を聞いて学ぶ

10/27(火) · 10:35

第6章 後半: 反復の仕組み、失敗、型、コピー

『日本語技術教科書 Vol.0』の第6章 後半。反復の仕組み、失敗、型、コピーを、2 人の掛け合いで解説する。

この回を聴く

AIこの記事は AI の Claude が書きました。

  • イテレータは走査の状態を持ち、一度消費すると空になる。ジェネレータの本体は、要求されたときに動く。
  • 例外処理は保護する範囲を狭くし、型ヒントは実行時の検査とは別の層にある。
  • 等しいことと同じ対象であることは別で、浅いコピーは内側を共有する。

教材の 6.4 節から 6.8 節まで。

6.4.1〜6.4.2 イテレータとジェネレータ

教材の 6.4.1 の節では、イテラブルは iter でイテレータを取り出せる対象、イテレータは next で次の値を返し、尽きると StopIteration で知らせるものとされる。リストは取り出すたびに新しい走査状態を作れるが、取り出したあとの 1 つのイテレータは消費した分を覚えていて、戻らない。10、20、30 のリストで next を 1 回呼ぶと 10 が出て、残りを読むと [20, 30]、もう一度読むと空になる。元のリストから要素が消えたのではなく、走査の位置が進んだだけである。

for はこの終了の通知を内部で処理するので、例外は表に出ない。手動の next で尽きると例外が出る。既定値を渡せば例外は避けられるが、その値が本物のデータにも現れるなら、見分ける仕掛けが別に要る。反復できるという性質だけでは、走査を繰り返せるのか、長さを持つのかは分からない。検査と集計で 2 回読む関数に単発のイテレータを渡すと、2 回目は何も得られない。

yield を含む関数を呼んでも、普通は本体がすぐには動かず、ジェネレータのオブジェクトが返る。next を呼んで初めて本体が進み、yield の所で値を渡して止まる。教材の例では、作成の表示が先に出て、開始と 4 がその後に出る。変換の失敗も要求されたときに起きるため、作成時の try では捕まえられないことがある。ジェネレータ式は結果のリストを丸ごと作らない。しかし元の入力が大きなリストならその場所は使われたままで、内部の局所変数や参照先も生き残りうる。小さく済むと言い切れるわけではない。

6.5.1 例外処理

try の中に失敗しうる処理を置き、except でその型の例外への対応を書く。else は何事も無く末尾まで進んだ場合の処理、finally は経路を問わない後始末の置き場である。ただしプロセスが強制終了されるときは、動く保証が無い。通常の処理を else に置くのは、そこで起きた別の不具合を変換の失敗と取り違えないためで、保護する範囲を狭くするという考え方に沿う。

except Exception で広く捕まえて無視すると、名前の書き間違いやデータの壊れまで見えなくなる。KeyboardInterrupt や SystemExit は Exception の仲間に入らないので、停止の要求まで受け止めるかどうかは別に考える。扱えない例外は raise で送り直す。finally に return を書くと、本来の戻り値や例外を上書きして見えなくしかねない。with は終了処理を構造化するが、処理全体の巻き戻しまでは保証しない。

6.6.1 型ヒント

型ヒントは、引数や戻り値に期待する型を書き添える仕組みである。静的検査は、動かす前にソースの食い違いを探す作業だ。一方、Python の実行系は、注釈だけを根拠に引数の型を確かめることは通常しない。教材の例では、文字列か None を受ける関数へ整数の 12 を渡すと、静的検査器なら不一致を指摘できるが、実行では整数への変換が通って 12 になる。注釈に意味が無いのではなく、受け持つ検査の層が違う。注釈からは、文字列が UTF-8 として正しいか、整数が範囲に収まるか、リストが空でないかも、通常は分からない。

6.7.1〜6.7.2 等価性、同一性、コピー

等価性は値が等しいか、同一性は同じ物を指しているかで、問いが違う。内容が同じ 2 つのリストは等しいが、同じ対象ではない。None は唯一の対象なので is で調べる。小さい整数や一部の文字列は実装が使い回して同じ対象になることがあるが、結果は実装やコンパイルのまとまりで変わりうるため、数の比較に is は使わない。

浅いコピーは外側の入れ物だけを作り直し、要素への参照は共有する。深いコピーは参照先までたどって作り直すので、時間も容量も多くかかる。リストのリストで、浅いコピーの b と深いコピーの c を作ってから元の最初の行に 9 を足すと、元と b には現れ、c には現れない。[[0] * 3] * 2 は、同じ内側のリストを 2 か所から指す形になり、片方の行を書き換えるともう片方も変わる。独立した 2 行は、内側を毎回作る内包表記で得られる。

6.8.1 小さなトークン集計器

章の締めの 6.8 では、UTF-8 のバイト列を空白で区切り、出現数を数える小さな集計器を読む。ここでの単位は空白で区切った文字列で、LLM のサブワードの分割とは別であり、日本語の分かち書きも扱わないと教材は断る。処理は、復号、NFC の正規化、分割、辞書での計数、頻度の多い順の整列で、同じ頻度なら文字列の昇順にする。頻度だけで並べると入力の順という偶然に頼るためである。不正なバイト列は復号の段階で止まり、一部だけの頻度は返らない。

この実装は入力全体、復号した文字列、分割結果、辞書、整列結果を処理中に持つので、巨大なファイルの逐次処理ではない。入力をジェネレータで読んでも、種類の多いトークンを辞書が持つ分は増え続ける。教材の 6.8.1 の節の行ごとの説明の終わりには、ごく短い一文があり、短い書き方なので補って読むと、位置の 0 と値が無い None は別のもの、という趣旨になる。

次の第 7 章は、リストや辞書の中身のつくりと、操作にかかる手間へ進む。

出典

  • オーナー(AI で作った教材、Web には公開していない) 日本語技術教科書 Vol.0 コンピュータからPython・機械学習・LLMへ 第6章 2026-10-18