1-01 値・型・変数
このページはプロモーション(広告)を含みます。
プログラムが扱う「値」と、その種類である「型」、値に名前を付ける「変数」の仕組みを、金額計算の落とし穴まで含めて学びます。
この章でできるようになること
- 式・値・型の関係を説明し、type() で値の型を確かめられる
- 変数を「値に付ける名前札」として説明し、代入と再代入の動きを予想できる
- 算術演算(/ と // と %、**)の結果を型も含めて予想できる
- 0.1 + 0.2 が 0.3 にならない理由を説明し、金額を整数(円)や Decimal で正しく計算できる
- 文字列と数値の型変換を行い、変換に失敗する入力を見分けられる
- AIが書いたコードの変数名・型・金額計算を読んで、危ない箇所を指摘できる
ここから Python の本格的な学習に入ります。最初の章で扱うのは、プログラムの一番小さな部品である「値」と、その種類を表す「型」、そして値に名前を付ける「変数」です。地味に見えますが、AIが書いたコードを読むとき、最初に確かめるのは「この名前には何が入っているのか」「それは数なのか文字なのか」です。ここがあいまいなままだと、後の章のどこかで必ずつまずきます。
実務でも、この章の内容はそのまま事故につながります。たとえば、CSVから読んだ売上が文字列のままで合計できない。税込価格を小数で計算して1円ずれる。割引後の金額を切り捨てたつもりが、なぜか1円少なくなる。これらはすべて「値と型」の理解で防げます。この章では、なぜそうなるのかという仕組みまで踏み込みます。
前の章(→ 0-01 AIエンジニアという仕事)では print で文字を表示しました。今回はその print の中に書いていた「もの」の正体を明らかにします。章の最後には、LLMアプリで値と型がどう現れるか(モデルの返答は常に文字列である、など)もつなげます。
python predict と書かれたコードは、実行する前に出力を紙かメモに予想してください。予想が外れたところが、まさに理解が足りない場所です。外れるのは失敗ではなく、学習が起きている合図です。
式と値:Pythonは「計算して値を作る」
まずは動かしてみます。次のコードは、ある商品の売上を計算して表示します。
アプリで実行できるコード
print(1980 * 3)
print("ノートPCスタンド")
print(1980 * 3 + 500)1980 * 3 のように、計算して何かを生み出す書き方を式(expression)と呼びます。式を計算した結果が値(value)です。1980 * 3 という式は 5940 という値になります。"ノートPCスタンド" も式の一種で、計算するまでもなく、それ自身が値です。
Python がしていることは、突き詰めると「式を計算して値を作り、その値を使って次の計算をする」ことの繰り返しです。1980 * 3 + 500 は、先に 1980 * 3 を計算して 5940 を作り、次に 5940 + 500 を計算して 6440 を作ります。算数と同じく、掛け算・割り算は足し算・引き算より先に計算されます。順番を変えたいときは丸かっこ () を使います。
実行前に出力を予想する
print(1980 * (3 + 500))
print((1980 + 20) * 3)
print(10 - 4 - 3)最初の行は丸かっこの中の 3 + 500 が先なので、1980 * 503 です。最後の 10 - 4 - 3 は左から順に計算され、(10 - 4) - 3 で 3 になります。10 - (4 - 3) の 9 ではありません。同じ優先度の演算は左から順に計算する、という規則があるからです。
Google Colab などのノートブックでは、セルの最後に式だけを書くと値が自動で表示されます。このアプリのコード実行では、最後の式は表示されません。値を見たいときは必ず print() で囲んでください。
print は値を画面に出す道具です。値そのものではありません。この区別は → 1-07 関数 で「return と print の違い」として改めて重要になります。今の段階では「計算は式がする、表示は print がする」と覚えておけば十分です。
値には型がある
値には種類があります。この種類を型(type)と呼びます。Python で最初に覚える型は5つです。
| 型の名前 | 読み方 | 例 | 何を表すか |
|---|---|---|---|
int |
イント(整数) | 1980, 0, -300 |
小数点のない数。個数、円単位の金額、件数 |
float |
フロート(浮動小数点数) | 0.1, 3.14, 2.0 |
小数点を含む数。比率、平均、温度 |
str |
ストリング(文字列) | "東京", "1980", "" |
文字の並び。名前、住所、問い合わせ文 |
bool |
ブール(真偽値) | True, False |
はい/いいえ。条件を満たすか |
NoneType |
ノン | None |
「値がない」ことを表す特別な値 |
値の型は type() で確かめられます。予想してから実行してください。
実行前に出力を予想する
print(type(1980))
print(type(1980.0))
print(type("1980"))
print(type(True))
print(type(None))1980 と 1980.0 と "1980" は、人間の目には同じ「1980」に見えます。しかし Python にとっては、整数・浮動小数点数・文字列という、まったく別の種類の値です。表示の <class 'int'> の class は「型」とほぼ同じ意味だと思ってかまいません(→ 1-12 クラスとオブジェクト で正体が分かります)。
なぜ型があるのか
型があるのは、型によって「できること」が違うからです。数なら足し算ができます。文字列なら、文字をつなげたり、一部を取り出したりできます。同じ + という記号でも、型によって意味が変わります。
実行前に出力を予想する
print(1980 + 20)
print("1980" + "20")数どうしの + は足し算、文字列どうしの + は連結(つなげること)です。だから "1980" + "20" は "198020" という6文字の文字列になります。これは「CSVから読んだ金額を足したら、とんでもなく大きな数になった」という実務の事故の正体です。CSVやフォームから読んだ値は、見た目が数字でも文字列であることが多いのです。
では、数と文字列を足すとどうなるでしょうか。
print("合計: " + 5940)TypeError: can only concatenate str (not "int") to str型エラー(TypeError)が起きます。メッセージは「文字列には文字列しか連結できません(int は連結できません)」という意味です。Python は「たぶん文字にしたいのだろう」と勝手に推測しません。推測しないおかげで、間違いがその場で表に出ます。直し方は後の「型変換」の節で扱います。
症状:数を足したはずなのに "35" のような文字がつながった結果になる。あるいは TypeError が出る。
原因:見た目が数字の文字列("3")と、本物の整数(3)を混同している。CSV、Excel からのコピー、Webフォーム、LLMの返答は、基本的にすべて文字列で届きます。
対処:値が怪しいときは print(type(値)) で型を確かめる。計算の前に int() や float() で数に変換する。表示するときに型を見分けたいなら print(repr(値)) を使うと、文字列には引用符が付いて区別できます。
AIのコードで見かける isinstance
型を確かめる方法として、AIが書いたコードでは type() より isinstance() のほうをよく見かけます。isinstance(値, 型) は「その値がその型か」を True / False で答えます。
アプリで実行できるコード
print(isinstance(1980, int))
print(isinstance("1980", int))
print(isinstance(1980.0, (int, float)))3行目のように、型を丸かっこで並べると「どれかに当てはまるか」を調べられます。isinstance は、受け取った値が想定どおりの型かを確かめる「入口の検査」でよく使われます。読むときは「ここで型を確かめて、違ったら別の処理をしようとしているのだな」と理解できれば十分です。条件によって処理を分ける書き方は → 1-05 条件分岐 で学びます。
None は「まだない」を表す
None は「値がない」ことを表す特別な値です。0 でも空の文字列 "" でもありません。たとえば顧客データで「電話番号が未登録」を表すとき、0 を入れると「0番という番号」と区別できません。"" を入れると「空欄で登録された」のか「まだ聞いていない」のかが曖昧になります。None は「そもそも値が存在しない」ことをはっきり示します。
アプリで実行できるコード
phone = None
email = ""
points = 0
print(repr(phone), repr(email), repr(points))
print(type(phone), type(email), type(points))LLM の API でも None はよく出てきます。モデルが文章ではなくツールの呼び出しを返したとき、本文(content)が None になる、という形です(→ 6-04 ツール呼び出し)。None の扱い方は → 1-05 条件分岐 で詳しく学びます。
変数は「名前札」
計算した値を後で使いたいとき、値に名前を付けます。これが変数(variable)です。名前を付けることを代入(assignment)と呼び、= で書きます。
アプリで実行できるコード
unit_price = 1980
quantity = 3
subtotal = unit_price * quantity
print(subtotal)unit_price = 1980 は「1980 という値に unit_price という名前を付ける」という意味です。数学の「等しい」ではありません。Python の = は「右側を計算して、その値に左側の名前を付ける」という命令です。右から左へ読むと理解しやすくなります。
箱ではなく、名前札
変数はよく「値を入れる箱」にたとえられます。最初のうちはそれでも困りません。しかし Python の実際の仕組みに近いのは、値に名前札を結びつけるというイメージです。
名前札 値(メモリ上に作られたもの)
[unit_price] ----> 1980
[quantity] ----> 3
[subtotal] ----> 5940箱のたとえの限界は、「1つの値に2つの名前を付ける」ときに表れます。箱なら中身はどちらか一方にしか入りません。名前札なら、1つの値に何枚でも札を付けられます。
実行前に出力を予想する
price_a = 1980
price_b = price_a
price_a = 2200
print(price_a)
print(price_b)順に追いかけます。price_b = price_a は「price_a が指している値(1980)に、price_b という札も付ける」という意味です。この瞬間、1980 には2枚の札が付いています。次の price_a = 2200 は「price_a の札を外して、新しく作った 2200 に付け替える」という意味です。price_b の札は 1980 に付いたまま動きません。だから price_b は 1980 のままです。
price_b = price_a のあと price_a = 2200 のあと
[price_a] --+ [price_a] ----> 2200
+--> 1980 [price_b] ----> 1980
[price_b] --+「price_b = price_a で、2つの変数がつながった」と考えると、price_b も 2200 になると予想してしまいます。実際には、つながっているのは名前どうしではなく、名前と値です。この名前札のモデルは → 1-03 リストとタプル で、もっと重要になります。中身を書き換えられる値(リスト)に2枚の札が付いていると、片方の札から書き換えた変更がもう片方からも見える、という現象が起きるからです。
再代入と「自分自身を使った更新」
同じ名前に別の値を付け直すことを再代入と呼びます。よく使うのが、今の値をもとに新しい値を作る書き方です。
実行前に出力を予想する
order_count = 10
order_count = order_count + 1
order_count = order_count * 2
print(order_count)order_count = order_count + 1 は、数学なら成り立たない式です。しかし Python では「右側を先に計算する」ので問題ありません。右側の order_count + 1 は、その時点の order_count(10)を使って 11 になります。その 11 に order_count の札を付け替えます。次の行では 11 * 2 で 22 になります。
この書き方は頻出なので、短く書く記法があります。order_count += 1 は order_count = order_count + 1 と同じ意味です。-=、*=、/= も同様です。AIが書くコードでは、件数を数えるときや合計を積み上げるときに必ず出てきます。
値を追いかける表を書く
変数が何度も付け替えられるコードは、頭の中だけで追うと必ずどこかで混乱します。そういうときは、紙やメモに「1行実行するごとに、各変数が何を指しているか」の表を書きます。これをトレース表と呼びます。
行 order_count
order_count = 10 10
order_count = order_count + 1 11
order_count = order_count * 2 22地道な方法ですが、プロのエンジニアも複雑なバグを追うときに同じことをします。AIが書いた数十行のコードで「どこで値がおかしくなったのか」を探すときも、怪しい行の前後に print(変数名, repr(変数)) を入れて、トレース表を実際の出力で作ります。予想した表と実際の出力が食い違った行が、バグの場所です(→ 3-06 AIと一緒にデバッグする)。
存在しない名前を使うと
札を付けていない名前を使うと、エラーになります。
total = subtotl * 1.1NameError: name 'subtotl' is not defined. Did you mean: 'subtotal'?名前エラー(NameError)は「その名前は定義されていない」という意味です。ほとんどの場合、原因はタイプミスか、代入する前に使っていることです。最近の Python は「もしかして subtotal のことですか」と候補まで示してくれます。エラーメッセージは敵ではなく、手がかりです。読み方は → 1-08 エラーを読み、直す で体系的に学びます。
Python のすべての値は、メモリ上に作られた「オブジェクト」です。変数はオブジェクトへの参照(どこにあるかの情報)を持っています。2つの名前が同じオブジェクトを指しているかは is で確かめられます。
アプリで実行できるコード
a = 1980
b = a
print(a is b)
b = 2200
print(a is b)最初の a is b は、2つの札が同じ値に付いているので True です。b を付け替えた後は別々の値を指すので False になります。is は「同じものか」、== は「値が等しいか」を調べます。数や文字列の比較には == を使い、is は None との比較(x is None)など特別な場面で使う、と覚えてください。小さな整数では、Python が内部で同じオブジェクトを使い回すことがあるため、is の結果が直感と違うことがあります。だからこそ数の比較に is を使ってはいけません。
算術演算:割り算は3種類ある
数の計算に使う記号(演算子)をまとめます。
| 演算子 | 意味 | 例 | 結果 |
|---|---|---|---|
+ |
足し算 | 1980 + 20 |
2000 |
- |
引き算 | 5000 - 1980 |
3020 |
* |
掛け算 | 1980 * 3 |
5940 |
/ |
割り算 | 10 / 4 |
2.5 |
// |
割り算の商(切り捨て) | 10 // 4 |
2 |
% |
割り算の余り | 10 % 4 |
2 |
** |
べき乗 | 2 ** 10 |
1024 |
注意が必要なのは / です。予想してください。
実行前に出力を予想する
print(10 / 2)
print(type(10 / 2))
print(10 // 2)
print(7 // 2, 7 % 2)/ は、割り切れる場合でも結果が必ず float になります。10 / 2 は 5 ではなく 5.0 です。これは「割り算の結果は一般に小数になりうるので、型を1つに揃えておく」という Python 3 の設計です。整数の結果がほしいときは // を使います。
// と % の実務での使い道
//(商)と %(余り)は、業務の計算でよく組み合わせて使います。たとえば在庫を箱詰めする場面です。
アプリで実行できるコード
stock = 50 # 在庫数
per_box = 12 # 1箱に入る数
full_boxes = stock // per_box
leftover = stock % per_box
print("満杯の箱:", full_boxes, "箱")
print("箱に入らない端数:", leftover, "個")
# 全部を出荷するのに必要な箱の数(切り上げ)
boxes_needed = (stock + per_box - 1) // per_box
print("必要な箱:", boxes_needed)
print("ちょうど48個なら:", (48 + per_box - 1) // per_box)では「全部を出荷するには何箱必要か」はどう計算するでしょうか。端数が1個でもあれば、もう1箱必要です。上のコードの後半が、Python で切り上げの割り算をするときの定番の書き方です。
stock + per_box - 1 は「端数が1個でもあれば次の倍数を超えるように、あらかじめ per_box - 1 を足しておく」という工夫です。50個なら (50 + 11) // 12 = 61 // 12 = 5 箱。48個なら (48 + 11) // 12 = 59 // 12 = 4 箱で、ぴったりのときに余分な箱を数えません。math.ceil(stock / per_box) という書き方もありますが、こちらは一度 float を経由するため、非常に大きな数では誤差の心配があります。整数だけで済む計算は整数だけで行うのが安全です。
% は「偶数か奇数か」(n % 2 == 0)、「10件ごとに区切る」(i % 10 == 0)、「曜日の計算」などにも使います。
型が混ざったとき
int と float を混ぜて計算すると、結果は float になります。
実行前に出力を予想する
print(1980 * 2)
print(1980 * 2.0)
print(2 ** 3, 2 ** 0.5)
print(3 * "ab")1980 * 2.0 は 3960.0 です。情報を失わない方向(より広く表せる float)に揃える、という規則です。最後の 3 * "ab" は、文字列の「繰り返し」で "ababab" になります。区切り線を "-" * 30 で作る、といった使い方をします。
// は「小さい方へ切り捨てる(床関数)」ので、負の数では直感とずれます。-7 // 2 は -3 ではなく -4 です。そして a == (a // b) * b + a % b が常に成り立つように、-7 % 2 は 1 になります。返金や差額の計算で負の数を割るときは、この挙動に注意してください。0 の方向へ切り捨てたいなら int(-7 / 2) で -3 になりますが、float を経由する点に注意が必要です。
浮動小数点の誤差と、金額計算の鉄則
この章で最も重要な節です。次のコードの出力を予想してください。
実行前に出力を予想する
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)多くの人が 0.3 と True を予想します。実際には 0.30000000000000004 と False が表示されます。これは Python のバグではありません。JavaScript でも Java でも Excel の内部でも、ほぼすべてのコンピュータで同じことが起きます。
なぜ 0.1 を正確に表せないのか
仕組みを、身近な例から考えます。10進数で 1 / 3 を書くと 0.3333… と無限に続きます。どこかで打ち切れば、必ず少しずれます。10進数では、分母が 2 と 5 だけでできている分数(1/2、1/4、1/5、1/10 など)しか、有限の桁で書けません。
コンピュータは数を 2進数(0 と 1)で保存します。2進数で有限の桁で書けるのは、分母が 2 だけでできている分数(1/2、1/4、1/8 など)だけです。ところが 0.1 は 1/10 で、分母に 5 が含まれます。だから 2進数にすると 0.0001100110011… と無限に続き、どこかで打ち切るしかありません。
float は、およそ 10進数で15〜17桁分の精度で数を保存します(正確には 2進数で53桁)。0.1 と書いた瞬間、実際に保存されるのは「0.1 に最も近い、2進数で表せる数」です。小数点以下を20桁まで表示させると、ずれが見えます。
アプリで実行できるコード
print(format(0.1, ".20f"))
print(format(0.2, ".20f"))
print(format(0.3, ".20f"))0.1 も 0.2 もわずかに大きめに保存されています。足すとずれが積み重なり、0.3 として保存される数(これもわずかにずれている)とは別の数になります。普段 print(0.1) が 0.1 と表示されるのは、Python が「保存された数に戻せる最短の表記」を選んで見せているからです。表示が 0.1 でも、中身は正確な 0.1 ではありません。
金額で起きる実際の事故
「小数点以下16桁目のずれなど、どうでもいい」と思うかもしれません。問題は、このずれが切り捨てと組み合わさったときに1円の差になることです。1,300円の商品を3割引にする計算をしてみます。
実行前に出力を予想する
price = 1300
discounted = int(price * 0.7)
print(price * 0.7)
print(discounted)正しい答えは 910円です。しかし 1300 * 0.7 は 909.9999999999999 と、ほんのわずかに小さく計算されます。int() は小数点以下を切り捨てるので、結果は 909円になります。1件なら1円ですが、数万件の請求をまとめて処理すると、合計が帳簿と合わなくなります。そして、この種のバグはテストで偶然見つかりにくいのが厄介な点です。1290円、1300円、1310円……と特定の金額だけで起きるからです。
鉄則:金額は「整数の円」で計算する
日本円の金額は、整数(int)で扱うのが基本です。割合の計算は「掛けてから割る」順に、整数だけで行います。
アプリで実行できるコード
price = 1300
discounted = price * 70 // 100 # 3割引 = 70%
print(discounted)
unit_price = 1980
tax = unit_price * 10 // 100 # 消費税10%(1円未満切り捨て)
print(unit_price + tax)price * 70 // 100 は、1300 * 70 = 91000 を先に整数で計算し、それを 100 で割った商を取ります。途中で float を一度も通らないので、誤差が入り込む余地がありません。
小数の計算がどうしても必要なとき(ドル建ての価格、利率、按分など)は、標準ライブラリの decimal(デシマル)を使います。Decimal は10進数のまま計算する型です。
アプリで実行できるコード
from decimal import Decimal, ROUND_HALF_UP, ROUND_DOWN
print(Decimal("0.1") + Decimal("0.2"))
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
amount = Decimal("2178.5")
print(amount.quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # 四捨五入
print(amount.quantize(Decimal("1"), rounding=ROUND_DOWN)) # 切り捨てquantize(Decimal("1")) は「1の位まで(小数点以下0桁)に揃える」という意味で、rounding= で端数の処理方法を指定します。from decimal import ... は「decimal という道具箱から、これらの部品を取り出して使う」という意味です(→ 1-10 モジュールと標準ライブラリ)。
症状:Decimal を使ったのに、誤差が消えない。
原因:Decimal(0.1) と書くと、先に 0.1 が float として作られ、ずれた値のまま Decimal に変換されます。print(Decimal(0.1)) を実行すると 0.1000000000000000055511151231257827021181583404541015625 と表示されます。
対処:Decimal には必ず文字列で数を渡します。Decimal("0.1") と書きます。AIが書いたコードで Decimal(0.1) や Decimal(price * 1.1) を見たら、その時点で誤差が入っていると読んでください。
round() は「四捨五入」ではない
もう1つ、よくある誤解を確かめます。
実行前に出力を予想する
print(round(0.5), round(1.5), round(2.5), round(3.5))学校で習った四捨五入なら 1 2 3 4 です。実際は 0 2 2 4 になります。Python の round() は、ちょうど半分のときに偶数の側へ丸める「偶数丸め(銀行丸め)」を使います。大量のデータを丸めたときに、切り上げ側に偏らないようにするための方式です。さらに round(2.675, 2) は 2.68 ではなく 2.67 になります。2.675 という float が、実際には 2.67499999… として保存されているからです。
実務では、1円未満の端数を「切り捨て」「切り上げ」「四捨五入」のどれにするかは、会社のルールや取引先との契約で決まっています。消費税の端数処理も事業者が選べますが、インボイス制度(適格請求書)では「1枚の請求書につき、税率ごとに1回」端数処理をする、とされています。商品1行ごとに丸めてから合計するのか、合計してから丸めるのかで、結果が変わることがあります。
詳細は国税庁の案内で確認してください。プログラムを書く側の責任は、次の3点です。
- 端数処理の方法を、仕様として明文化してもらう(推測で決めない)。
- 金額は
int(円)かDecimalで計算し、floatとround()を使わない。 - 境界のテスト(.5 ちょうど、0円、大きな金額)を用意する(→ 2-04 テスト)。
float が正しい道具である場面
誤解のないように付け加えると、float 自体は欠陥品ではありません。成約率、平均応答時間、類似度のスコア、機械学習の計算など、「ごくわずかな誤差が問題にならない」量には float が適しています。速く、扱いやすいからです。LLM の temperature(応答のばらつき具合)や、埋め込みベクトルの類似度(→ 4-08 ベクトルと埋め込み)も float です。
問題は「float どうしを == で比べる」ことと「float の金額を切り捨てる」ことです。float を比べたいときは、差が十分に小さいかを調べます。
アプリで実行できるコード
import math
print(math.isclose(0.1 + 0.2, 0.3))
print(abs((0.1 + 0.2) - 0.3) < 1e-9)1e-9 は 0.000000001(10のマイナス9乗)を表す書き方です。科学技術計算や機械学習のコードでよく見かけます。
型変換:文字列から数へ、数から文字列へ
外から入ってくるデータは、ほぼ文字列です。計算するには数に変換し、表示するには文字列に変換します。型の名前(int、float、str)を関数のように使うと変換できます。
アプリで実行できるコード
qty_text = "3" # フォームから来た数量
price_text = "1980" # CSVから来た単価
qty = int(qty_text)
price = int(price_text)
print(qty * price)
print("合計: " + str(qty * price) + "円")int("3") は文字列 "3" を整数 3 に変換し、str(5940) は整数を文字列 "5940" に変換します。前の節で TypeError になった "合計: " + 5940 は、str() で文字列にしてから連結すれば動きます。もっと読みやすい書き方(f文字列)は次の章で学びます(→ 1-02 文字列を扱う)。
変換できるもの、できないもの
変換は、いつも成功するわけではありません。予想してください。どれがエラーになるでしょうか(エラーになる行があると、そこで実行が止まるので、この例ではエラーにならないものだけを並べています)。
実行前に出力を予想する
print(int(" 42 "))
print(int("123"))
print(int(3.99))
print(float("1e3"))
print(bool("False"))1行目:前後の空白は無視されるので 42 になります。
2行目:全角数字の "123" も、Python は数字として理解し 123 に変換します。日本語の入力フォームでは全角数字が混ざりがちなので、知っておくと役立つ性質です(ただし全角のカンマやマイナス記号は別問題です。→ 1-02 文字列を扱う で扱う正規化が必要になります)。
3行目:int(3.99) は四捨五入ではなく、小数点以下の切り捨てで 3 です。
4行目:"1e3" は「1×10の3乗」なので 1000.0 です。
5行目が最大の落とし穴です。bool("False") は True になります。bool() は文字列の中身を読まず、「空の文字列なら False、1文字でもあれば True」と判定するからです。CSVやフォームで "False" や "0" という文字が届いたとき、bool() で変換すると全部 True になってしまいます。正しくは text == "True" のように、文字列として比較します。
一方、次のような変換はエラーになります。
int("1,280") # カンマ入り
int("3.5") # 小数の文字列を int に
int("三") # 漢数字
int("") # 空文字
float("abc")ValueError: invalid literal for int() with base 10: '1,280'値エラー(ValueError)は「型は合っているが、中身が受け付けられない」という意味です。int("3.5") が失敗する点は意外かもしれません。int() に渡す文字列は、整数の形をしている必要があります。小数の文字列を整数にしたいなら int(float("3.5")) のように2段階で変換します。カンマ入りの金額は、カンマを取り除いてから変換します("1,280".replace(",", "") で "1280" になります。→ 1-02 文字列を扱う)。
症状:テストデータでは動いたのに、本番のCSVで ValueError が出る。
原因:実データには、カンマ入りの数("1,280")、全角や空白混じり、空欄("")、"-" や "N/A" のような「値なし」の記号が混ざります。
対処:変換する前に、実データを何件か print(repr(値)) で見る。どんな形の入力がありうるかを仕様として決める。変換に失敗したときの扱い(その行を飛ばすのか、エラーとして報告するのか)を決める(→ 1-08 エラーを読み、直す、→ 1-11 ファイル・JSON・CSV)。
実務のコードでは、外から来たデータを、受け取った直後に一度だけ変換・検証するのが原則です。CSVを読んだ直後、フォームを受け取った直後、LLMの返答を受け取った直後です。そこから先のコードは「price は必ず int」と信じて書けるようになります。
逆に、文字列のままプログラムの奥まで運び、使う場所ごとに int() を書くと、変換し忘れる箇所が必ず出ます。どこかで "1980" + "20" のような連結が起き、しかもエラーにならずに静かに間違った値が流れます。この「入口で型を確定させる」考え方は、→ 1-13 型ヒントと Pydantic で道具として整います。AIにコードを書かせるときも「入力は最初に int に変換し、変換できなければエラーにする」と指示に含めると、コードの見通しがよくなります。
比較演算と真偽値
2つの値を比べる演算子は、結果として True か False(bool 型)を返します。
| 演算子 | 意味 | 例 | 結果 |
|---|---|---|---|
== |
等しい | 1980 == 1980 |
True |
!= |
等しくない | 1980 != 2200 |
True |
< > |
より小さい/大きい | 3 < 5 |
True |
<= >= |
以下/以上 | 5 >= 5 |
True |
= は代入、== は比較です。この2つの取り違えは、初心者が最もよくする間違いの1つです。Python では if x = 5: のように書くと SyntaxError(文法エラー)になるので、気づきやすくはなっています。
比較の結果も値なので、変数に入れられます。
アプリで実行できるコード
monthly_sales = 1_250_000
target = 1_000_000
achieved = monthly_sales >= target
print(achieved, type(achieved))
print(0 <= 75 <= 100) # 範囲のチェックはつなげて書ける1_250_000 のアンダースコアは、桁を読みやすくするための区切りで、1250000 と同じ値です。金額や件数の定数を書くときに便利です。0 <= 75 <= 100 のように比較をつなげると「0以上かつ100以下」という意味になります。数学の書き方と同じです。
型が違うものの比較
予想してください。
実行前に出力を予想する
print(1 == 1.0)
print(1980 == "1980")
print("10" < "9")
print("apple" < "banana")1 == 1.0 は数として等しいので True です。1980 == "1980" は、型が違うので False です。エラーにはならず、黙って False を返す点が危険です。CSVから読んだ文字列の商品コードと、プログラム内の整数の商品コードを比べると、見た目が同じでも一致しません。
"10" < "9" は True です。文字列の比較は、数の大小ではなく、先頭の文字から1文字ずつ辞書順で比べます。"1" は "9" より前なので、2文字目を見る前に決着します。売上の数字を文字列のまま並べ替えると、"10000" が "9000" より小さいと判定されて、ランキングがおかしくなります。数として比べたいなら、先に数に変換します。
Python では、True は 1、False は 0 として計算に使えます。True + True は 2 です。これを利用して、「条件を満たした件数」を sum() で数えるコードがよく書かれます(→ 1-09 内包表記とよく使う組み込み関数)。読めれば十分ですが、True と 1 が == で等しいことは覚えておくと、思わぬ一致に気づけます。
名前の付け方とコメント
コードは、書かれる回数より読まれる回数のほうがずっと多いものです。AIが書いたコードを自分が読み、自分が直したコードをAIが読みます。読み手にとって、変数名は最大の手がかりです。
Python の命名規則
Python の公式スタイルガイド(PEP 8)では、変数名はスネークケース(snake_case)で書きます。小文字の英単語をアンダースコアでつなぐ書き方です。
# 良い例
unit_price = 1980
monthly_sales = 1_250_000
is_member = True
inquiry_text = "返品したいのですが"
# 変更しない値(定数)は大文字
TAX_RATE_PERCENT = 10
MAX_RETRY = 3
# 避けたい例
x = 1980 # 何の値か分からない
UnitPrice = 1980 # クラス名の書き方(1-12)と紛らわしい
tanka = 1980 # ローマ字は読み手によって通じない名前の規則のうち、守らないとエラーになるもの(数字で始められない、- や空白を含められない、if や for などの予約語は使えない)と、守らないと読みにくくなるだけのもの(スネークケース、意味のある名前)があります。後者も同じくらい大切です。日本語の変数名(単価 = 1980)も文法上は使えますが、チームやツールとの相性から、業務のコードでは英語にするのが一般的です。
組み込みの名前を上書きしない
Python には最初から使える名前(print、type、int、str、sum、list など)があります。これらに代入すると、本来の機能が使えなくなります。
sum = 1980 + 2200 # 合計を sum という名前で保存した
print(sum([1, 2, 3])) # 後で本来の sum() を使おうとすると……TypeError: 'int' object is not callable「int 型の値は呼び出せません」という、一見わけの分からないエラーです。sum という札が、合計関数から整数 4180 に付け替えられたのが原因です。total、sales_sum のように別の名前を使えば防げます。type、id、input、list、dict、str も、うっかり変数名にしがちなので注意してください。
コメントは「なぜ」を書く
# から行末まではコメントで、Python は無視します。人間(とAI)への説明です。
# 悪いコメント:コードを読めば分かることを繰り返している
tax = price * 10 // 100 # price に 10 を掛けて 100 で割る
# 良いコメント:コードからは分からない理由を書く
# 経理規程により、消費税は1円未満切り捨て(float を通すと1円ずれるため整数で計算)
tax = price * 10 // 100コメントは「何をしているか」ではなく「なぜそうしているか」を書きます。「何を」は、良い変数名とコードそのものが語るべきことです。
AIが書いたコードの読み方と、LLMアプリでの値と型
この課程では、コードの多くをAIに書かせることになります。そのとき、この章の知識は「読んで危ない箇所を見つける目」として働きます。
この書き方を見たらこう読む
AIが書いたコードには、次のような書き方がよく出てきます。
TAX_RATE = 0.1
price: int = 1980
total = int(price * (1 + TAX_RATE))
count += 1
amount = Decimal(str(row["amount"]))1行ずつ、こう読みます。
TAX_RATE = 0.1:大文字なので定数。税率をfloatで持っている。この後、金額と掛けて切り捨てていないか確認する。price: int = 1980:: intは型ヒント(type hint)で、「この変数にはintを入れるつもり」という注釈です。Python は実行時にこれを強制しません(→ 1-13 型ヒントと Pydantic)。注釈と実際の値が食い違っていないかを読む。total = int(price * (1 + TAX_RATE)):floatの計算結果をint()で切り捨てている。この章で見た「1円ずれる」パターンそのもの。price * 110 // 100に直すべきか、端数処理の仕様を確認する。count += 1:件数を1つ増やしている。どこで0に初期化されているかを探す。Decimal(str(...)):floatを一度文字列にしてからDecimalにしている。Decimal(0.1)の問題を避ける、よく知られた書き方。
AIは、浮動小数点で金額を計算するコードを、ごく自然に書きます。学習データに price * 1.1 のようなコードが大量に含まれているからです。動くし、ほとんどのテストも通ります。だからこそ、読む側が気づく必要があります。
LLMアプリでは、値と型がこう現れる
LLM(大規模言語モデル)を使うアプリでは、この章の型がそのまま登場します。アプリ内の模擬LLM(llmsim)を使って確かめます。これは本物のLLMではなく、決まったルールで応答する練習用の模擬モデルです。呼び出し方と返ってくるデータの形は、本物の API(OpenAI 互換の Chat Completions API)と同じになっています。
アプリで実行できるコード
from llmsim import OpenAI
client = OpenAI() # 模擬モデル。本物の LLM は呼びません
resp = client.chat.completions.create(
model="sim-small",
messages=[{"role": "user", "content": "注文番号12345の在庫数を教えてください"}],
)
answer = resp.choices[0].message.content
print(answer)
print(type(answer))
print(resp.usage.prompt_tokens, type(resp.usage.prompt_tokens))
print(client.total_cost, type(client.total_cost))コードの細かい書き方は、今は分からなくてかまいません(→ 6-01 LLM API の基本 で学びます)。注目してほしいのは型です。
- モデルの返答(
answer)はstrです。モデルが「在庫は 3 個です」と答えても、それは文字列の一部です。計算に使うには、文字列から数字を取り出してint()で変換する必要があります。本物のモデルは「3個」「三個」「3 個程度」とさまざまな書き方で返してくるので、変換の失敗にも備えます(→ 6-03 構造化出力)。 - トークン数(
prompt_tokens)はintです。トークンとは、モデルが文章を処理する単位です(→ 5-02 トークンとコンテキスト)。 - 料金(
total_cost)はfloatです。ドル建ての非常に小さな値なのでfloatで扱われています。1.55e-05のような表示は、前に出てきた1e-9と同じ指数表記で、0.0000155 ドルという意味です(この料金は模擬の値で、実在の料金ではありません)。請求書のように1円単位で正確さが必要な集計では、Decimalに変換して扱います。
もう1つ、LLMアプリの設計で大切な原則があります。正確さが必要な計算は、LLMにさせずにコードで行うことです。LLMは文章を確率的に生成する仕組みなので、桁の多い掛け算や端数処理を、もっともらしいが間違った数字で答えることがあります(→ 5-05 LLMの得意・不得意と幻覚)。見積もり金額を答えるチャットボットなら、LLMには「どの商品を何個」という情報を読み取らせ、金額の計算はこの章で学んだ整数の計算で行います。LLMとコードの役割分担は、この課程を通して繰り返し出てくる考え方です。
AIは「説明させる」「問題を出させる」使い方をすると、学習の助けになります。答えのコードだけをもらうと、自分で読めるようにはなりません。
おすすめの使い方は3つです。
- 予想が外れた predict ブロックをAIに貼り、「なぜこの出力になるのか、初心者向けに、仕組みから説明して」と頼む。説明を読んだら、自分の言葉で説明し直して、AIに間違いを指摘してもらう。
- 「型と型変換について、出力を予想する問題を5問出して。答えは私が答えてから見せて」と頼む。
- AIが書いた金額計算のコードを貼り、「float の誤差で1円ずれる箇所がないか確認して」と頼む。
AIが間違えやすい点も知っておいてください。round() を「四捨五入する関数」と説明してしまうことがあります。また、Decimal(0.1) のような誤った使い方を、正しいコードとして提示することもあります。AIの説明は、必ず実際に実行して確かめてください。
あなたはPythonの家庭教師です。私はプログラミング初心者で、「値・型・変数」を学んでいます。
次の範囲から、出力を予想する問題を1問ずつ出してください:{int と float の割り算 / 型変換 / 変数の再代入 / 浮動小数点の誤差}
ルール:
- 問題は3〜5行の短いコードにする
- 私が答えるまで正解を見せない
- 私の答えが間違っていたら、正解を言う前に、どこで考え違いをしたかのヒントを1つ出す
- 業務の例(売上、在庫、顧客)を使う演習
演習:注文金額を計算する
次の注文について、小計・消費税・税込合計を計算してください。
- 単価
unit_priceは 1,980円、数量quantityは 3個 - 消費税は10%、1円未満は切り捨て
floatを使わず、整数だけで計算すること
subtotal(小計)、tax(消費税)、total(税込合計)という変数に、それぞれ int の値を代入してください。最後に print で税込合計を表示します。
解答例
unit_price = 1980
quantity = 3
subtotal = unit_price * quantity
tax = subtotal * 10 // 100
total = subtotal + tax
print("税込合計:", total)演習:CSVの文字列を数に直して集計する
CSVファイルから、ある商品の1行を読み込んだところ、値がすべて文字列で届きました。
price_text:単価(カンマ区切り)"1,280"qty_text:数量(前後に空白あり)" 4 "member_text:会員かどうか"False"
単価と数量を int に変換して price、qty に代入し、amount = price * qty を計算してください。会員かどうかは is_member に bool で代入します。bool(member_text) を使ってはいけません(理由は本文参照)。
カンマの除去には price_text.replace(",", "") を使えます(詳しくは次の章で学びます)。
解答例
price_text = "1,280"
qty_text = " 4 "
member_text = "False"
price = int(price_text.replace(",", ""))
qty = int(qty_text)
amount = price * qty
is_member = member_text == "True"
print(price, qty, amount, is_member)演習:箱の数と端数を求める
倉庫から商品を出荷します。1箱に per_box 個入ります。次の2つの注文について、必要な箱の数(端数があれば1箱追加)と、最後の箱に入る個数を求めてください。
- 注文A:
order_a = 50個 - 注文B:
order_b = 48個 per_box = 12
変数 boxes_a、boxes_b に必要な箱の数を、last_a、last_b に最後の箱に入る個数を代入してください。ちょうど割り切れるときは、最後の箱は満杯(12個)です。整数の演算(//、%、+、-)だけを使ってください。
解答例
per_box = 12
order_a = 50
order_b = 48
boxes_a = (order_a + per_box - 1) // per_box
boxes_b = (order_b + per_box - 1) // per_box
last_a = order_a - (boxes_a - 1) * per_box
last_b = order_b - (boxes_b - 1) * per_box
print(boxes_a, last_a, boxes_b, last_b)演習:AIが書いた割引計算のバグを直す
AIに「会員は30%引き、1円未満切り捨て」の計算を書かせたところ、次のコードが出てきました。1,300円の商品で 909円になってしまいます(正しくは 910円)。
float を使わない計算に直してください。変数 discounted_1300、discounted_1980、discounted_99 に、それぞれ 1,300円、1,980円、99円の割引後の金額を代入します。
解答例
DISCOUNT_PERCENT = 30
discounted_1300 = 1300 * (100 - DISCOUNT_PERCENT) // 100
discounted_1980 = 1980 * (100 - DISCOUNT_PERCENT) // 100
discounted_99 = 99 * (100 - DISCOUNT_PERCENT) // 100
print(discounted_1300, discounted_1980, discounted_99)演習:営業日報の数字をまとめる
営業日報のフォームから、次の値が文字列で届きました。
visits_text:訪問件数"12"deals_text:成約件数" 3 "sales_text:売上金額(円、カンマ区切り)"1,250,000"target_text:今月の目標(円、カンマ区切り)"1,000,000"
次の値を計算し、それぞれの変数に代入してください。
visits、deals、sales、target:それぞれintに変換した値close_rate:成約率(%)。成約件数 ÷ 訪問件数 × 100 をfloatで求める(ここでは誤差が問題にならないのでfloatでよい)avg_deal:成約1件あたりの平均売上(円、1円未満切り捨て、int)achieved:売上が目標以上ならTrue、そうでなければFalseshortfall:目標に対する不足額(円)。達成していれば0
最後に、次の3行をこの形で表示してください(数値は計算結果)。
成約率: 25.0 %
平均単価: 416666 円
目標達成: True
解答例
visits_text = "12"
deals_text = " 3 "
sales_text = "1,250,000"
target_text = "1,000,000"
# ① 入力を数に変換する
visits = int(visits_text)
deals = int(deals_text)
sales = int(sales_text.replace(",", ""))
target = int(target_text.replace(",", ""))
# ② 指標を計算する
close_rate = deals / visits * 100
avg_deal = sales // deals
achieved = sales >= target
shortfall = max(target - sales, 0)
# ③ 結果を表示する
print("成約率:", close_rate, "%")
print("平均単価:", avg_deal, "円")
print("目標達成:", achieved)理解度チェック
次のコードの出力は?
a = 100
b = a
a = a + 50
print(b)
- ① 150
- ② 100
- ③ 50
- ④ エラーになる
解答と解説
正解:②
b = a は「a が指している値 100 に b の札も付ける」という意味です。その後 a は新しい値 150 に付け替えられますが、b の札は 100 に付いたままです。「変数どうしがつながる」わけではありません。
print(7 / 7) の出力は?
- ① 1
- ② 1.0
- ③ 0
- ④ 7
解答と解説
正解:②
/ は割り切れる場合でも、結果は必ず float になります。整数の結果がほしいなら 7 // 7 と書きます。
1,300円の3割引後の金額(1円未満切り捨て)を求めるコードとして、最も適切なのは?
- ①
int(1300 * 0.7) - ②
round(1300 * 0.7) - ③
1300 * 70 // 100 - ④
1300 * 0.7 // 1
解答と解説
正解:③
1300 * 0.7 は 909.9999999999999 になるため、int() や // 1 で切り捨てると 909 になります。round() はこの例では 910 になりますが、切り捨てではなく丸めなので仕様と違い、別の金額で誤差が出ます。整数で掛けてから割る 1300 * 70 // 100 なら float を通らず、正確に 910 になります。
bool("False") の値は?
- ① False
- ② True
- ③ None
- ④ ValueError になる
解答と解説
正解:②
bool() は文字列の中身を読まず、空文字列 "" のときだけ False、それ以外はすべて True を返します。文字列の中身で判定したいときは text == "True" のように比較します。
次のうち、ValueError になるものはどれ?
- ①
int(" 42 ") - ②
int("123") - ③
int("3.5") - ④
float("3.5")
解答と解説
正解:③
int() に渡す文字列は、整数の形をしている必要があります。"3.5" は小数の形なので失敗します。前後の空白や全角数字は受け付けられます。小数の文字列を整数にしたいなら int(float("3.5")) と2段階にします。
0.1 + 0.2 == 0.3 が False になる理由として正しいのは?
- ① Python の足し算にバグがあるため
- ②
==は小数の比較に対応していないため - ③ 0.1 や 0.2 は2進数で有限の桁に表せず、保存した時点でわずかにずれているため
- ④ 0.3 が整数として扱われるため
解答と解説
正解:③
分母に 5 を含む 1/10 は、2進数では無限に続く小数になります。float はそれを最も近い値で保存するため、わずかなずれが生じ、足すとずれが表に出ます。Python 以外のほとんどの言語でも同じことが起きます。
AIが書いた次のコードを読んで、最も注意すべき点は?
sum = price_a + price_b
print("合計", sum)
- ① 組み込み関数
sumを上書きしており、後でsum()を使うとエラーになる - ②
printに複数の値を渡しているのでエラーになる - ③
sumは予約語なので SyntaxError になる - ④ 問題はない
解答と解説
正解:①
sum は予約語ではないので代入自体はできますが、組み込み関数の sum が使えなくなります。後で sum([...]) を呼ぶと TypeError: 'int' object is not callable という分かりにくいエラーになります。total などの別名を使います。
まとめ
- 式を計算すると値ができる。値には型(
int、float、str、bool、None)があり、type()で確かめられる。型によって+の意味などの「できること」が変わる。 - 変数は値に付ける名前札。
=は「右側を計算して、その値に左の名前を付ける」命令。b = aの後でaを付け替えても、bは元の値を指したまま。 /は常にfloatを返す。//は商、%は余り。切り上げの割り算は(n + k - 1) // k。0.1 + 0.2が0.3にならないのは、0.1 が2進数で正確に表せないため。floatどうしを==で比べず、math.isclose()などを使う。- 金額は整数の円で計算し、割合は「掛けてから
//で割る」。小数が必要ならDecimal("0.1")のように文字列から作る。round()は四捨五入ではない。端数処理は仕様として決めてもらう。 - 外から来るデータ(CSV、フォーム、LLMの返答)は文字列。
int()、float()、str()で変換し、ValueErrorになる入力とbool("False")の罠を知っておく。 - 変数名はスネークケースで意味が分かるように。組み込みの名前(
sum、list、typeなど)を上書きしない。コメントには「なぜ」を書く。 - AIは
floatで金額を計算するコードを自然に書く。int(price * 1.1)やDecimal(0.1)を見たら、誤差の入り口として読む。
参考資料
いずれも任意です。この章の内容はアプリ内で完結しています。
- Python チュートリアル 3. 形式ばらない Python の紹介:数・文字列・変数の基本。公式の日本語訳です。
- Python チュートリアル 15. 浮動小数点演算、その問題と制限:0.1 が正確に表せない理由の公式な説明です。
- decimal --- 十進固定小数点および浮動小数点算術:
Decimalと丸めモードの一覧です。 - PEP 8 -- Style Guide for Python Code:Python の公式スタイルガイド(英語)。命名規則の節だけ読めば十分です。
Groundwork AIエンジニア養成課程:AIエンジニアを目指す人のための全85章の課程。Pythonの基礎、Git・テスト、Claude CodeやCodexと一緒に開発する方法、LLMの仕組みとアプリ開発までを、ブラウザで動くPythonで。最初の6章は無料。全章は2,480円の買い切り。
アプリを無料で開く有料版と無料版の違い