現役文大生がつまづいたPython疑問【入門編】
はじめに
はじめまして!文系慶應SFCの1年生のナベと申します!
最近、情報基礎1・2でPythonを触り始めたんですが、まあ序盤からわからなくて手が止まる止まる。C++を少し触ったことがある分、「C++ではこうだったのに、Pythonだと違うじゃん」という混乱も多かったです。
分からないたびにAIへ質問していたので、そのログを読み返すと、自分がどこで引っかかったかがかなり生々しく残っていました。これ、たぶん自分だけじゃないはずです。同じところで止まっている人の助けになればと思って、当時の素朴な疑問をそのまま見出しにしてまとめたいと思います!
情報基礎で悩んでる人は共感してくれたらうれしいし、疑問があればaiに対して僕が実際に送った文章も見出しとしてちょくちょく載せてるので、こんな適当にきいていいんだみたいな形で見てもらえれば嬉しいです!
記事は2部構成にしました。この【入門編】では、型・リストと辞書・条件分岐など、Pythonを触って最初にぶつかる疑問をまとめます。関数やクラス、importなど、少し抽象度が上がるものは【発展編】に分けました。
1. 「3 / 3 = 1」なのに、なんで float 型になるの?
a = 3 / 3
print(a) # 1.0
print(type(a)) # <class 'float'>
まずここで止まりました。3 ÷ 3 は 1 です。どう見ても整数です。
でもPythonは、「答えが整数かどうか」ではなく、「どの計算方法を使ったか」で型を決めます。
-
/は普通の割り算。だから結果は float -
//は切り捨て除算。だから結果は int
print(3 / 3) # 1.0
print(3 // 3) # 1
print(10 / 3) # 3.3333333333333335
なるほど、と思ったのはここです。もし「割り切れたら int、割り切れなかったら float」みたいな仕様だと、同じ / の計算なのに結果の型が毎回変わります。それはそれで面倒くさい。だから / は最初から「割り算するなら float」と決め打ちしているわけです。
Python、意外と筋が通っています。
2. 「3.14 * 100」が 314 じゃなくて 314.0 になるのはなんで?
x = 3.14
print(x * 100) # 314.0
これも最初は「いや、314でよくない?」と思いました。
理由は単純で、x = 3.14 の時点で x は float です。float と int を掛けると、結果も float になります。
print(3 * 100) # 300
print(3.0 * 100) # 300.0
print(int(3.14 * 100)) # 314
整数でほしければ int() で変換します。ただし注意点があって、int() は四捨五入ではなく切り捨てです。int(1.9) は 2 ではなく 1 です。ここ、地味に踏みました。
このへんでようやく分かってきたのは、Pythonは「見た目が整数っぽいか」ではなく、「型が何か」で動いているということです。ここが分かると、この後の話もかなりつながってきます。
3. 辞書は「作るときは {}」なのに「取り出すときは []」なの、分かりにくい
scores = {"佐藤": 85, "鈴木": 90}
print(scores["佐藤"]) # 85
いや、本当に最初はそう思いました。辞書なんだから {} で統一してくれ、と。
でも役割が違います。
-
{}は「作る」ときの記号 -
[]は「取り出す」ときの記号
[] はリストでも使います。つまりPythonの中では、[] は「中から何かを取り出す場所」という共通ルールなんです。
fruits = ["りんご", "みかん", "ぶどう"]
print(fruits[0]) # りんご
print(scores["佐藤"]) # 85
リストでは番号を指定して取り出す。辞書ではキーを指定して取り出す。やっていることは同じです。
覚え方はこれです。
{}は作る。[]は取り出す。
scores{"佐藤"} とは書けません。ここはもう、そういうルールとして飲み込むしかないです。
4. インデックスが「-1」から始まるの、なんで?キモくない?
fruits = ["apple", "banana", "orange", "grape"]
print(fruits[-1]) # grape
正直、最初はキモいと思いました。なんで後ろから数えると -1 なんだ、と。
でも理由を聞くと、まあ納得はできます。もし末尾を 0 にしたら、fruits[0] が先頭なのか末尾なのか分からなくなります。0 は先頭用にすでに使われているので、後ろからは -1、-2、-3 と数えるしかないわけです。
| 先頭から | 値 | 後ろから |
|---|---|---|
| 0 | apple | -4 |
| 1 | banana | -3 |
| 2 | orange | -2 |
| 3 | grape | -1 |
仕組みとしては、「リストの長さ + 負のインデックス」が先頭からの位置になります。fruits[-1] は 4 + (-1) = 3 番目、つまり最後です。
最初は違和感しかなかったんですが、「最後の要素がほしい」という場面ではかなり便利です。fruits[len(fruits) - 1] と長く書くより、fruits[-1] のほうが圧倒的に楽です。
キモいけど、便利。そういう機能です。
5. スライスの「終了位置は含まない」のが納得いかない
data = [10, 20, 30, 40, 50, 60]
print(data[1:4]) # [20, 30, 40]
data[1:4] と書いたら、普通は「1番から4番まで」と読みたくなります。でもPythonのスライスは違います。「1番から始めて、4番の直前で止める」です。
これが当時いちばん納得いきませんでした。なんで含まないんだ、と。
一番分かりやすかった理由は、リストを前半と後半に分けるときの例です。
print(data[:3]) # [10, 20, 30]
print(data[3:]) # [40, 50, 60]
境界が両方とも 3 になります。重ならないし、抜けもありません。もし終了位置を含む仕様だったら、前半を data[:2] にするなど、いちいちずらして考える必要があります。
あと、取れる個数が「終了位置 - 開始位置」で分かるのも便利です。data[1:4] は 4 - 1 = 3 個です。
「4番まで」ではなく、「4番の手前で止める」。そう読み替えたら、だいぶスッと入ってきました。
ちなみに、この考え方はPythonだけの変な仕様ではなく、C++のイテレータなどでも似た設計があります。知ったときはちょっと安心しました。
6. 「scores.items()」の items() って何?
scores = {"佐藤": 85, "鈴木": 90, "田中": 78}
for name, score in scores.items():
print(f"{name}さんの点数: {score}点")
実行結果はこうなります。
佐藤さんの点数: 85点
鈴木さんの点数: 90点
田中さんの点数: 78点
items() は、辞書の「キーと値のペア」を取り出すメソッドです。
scores.items() からは、("佐藤", 85)、("鈴木", 90)、("田中", 78) のようなペアが順番に出てきます。それを name と score の2つの変数に分けて受け取っているだけです。
ここも最初は呪文みたいに見えました。でも分解すると、やっていることはかなり素直です。
-
scoresは辞書 -
items()でキーと値のペアを出す -
for name, score in ...で、ペアを名前と点数に分ける - あとは1人ずつ表示する
変数名は自由です。for a, b in scores.items(): でも動きます。ただ、それだと何を入れているのか分かりにくいので、name と score のように意味のある名前をつけるのが普通です。
7. 「elif」は最初に True になったやつだけ処理される、で合ってる?
これは自分の理解が合っていました。
x = 75
if x >= 90:
print("A")
elif x >= 80:
print("B")
elif x >= 70:
print("C")
elif x >= 60:
print("D")
else:
print("E")
この場合、実行されるのは "C" だけです。elif が10個あっても100個あっても、上から順に見ていって、最初に True になったところだけを実行します。残りは見ません。
ただし、注意点があります。elif ではなく、if を並べると動きが変わります。
x = 100
if x >= 90:
print("A")
if x >= 70:
print("B")
この場合は "A" と "B" の両方が出ます。
elif は「1つの条件分岐の続き」です。新しい if を書いたら、「別の条件分岐が始まった」という扱いになります。ここはかなり大事でした。
8. 「if is_rain:」は比較してないのに意味あるの?
is_rain = False
temperature = 30
if is_rain or temperature > 30:
print("家で過ごす")
else:
print("外で遊ぶ")
最初は、「is_rain のあとに何も比較してないじゃん」と思いました。== True とか書かないと判定できないのでは、と。
でも is_rain は bool 型の変数です。中には最初から True か False が入っています。だから比較はいりません。
if is_rain: は、実質 if is_rain == True: と同じです。
そして if が最終的に見ているのは、「条件が True か False か」だけです。
-
if is_rain:は、変数そのものが True かを見る -
if temperature > 30:は、比較した結果が True かを見る
上の例だと、is_rain は False、temperature > 30 も False です。だから or の結果も False になって、else の「外で遊ぶ」が実行されます。
or は「どちらか一方が True なら True」です。両方 False なら False。当たり前なんですが、コードで見ると急に難しく感じるやつでした。
9. 「k++」みたいに簡単に1足すやつないの?
あります。
Pythonでは k += 1 と書きます。
k = 5
k += 1
print(k) # 6
C++の k++ に慣れていると、最初は少し面倒に感じます。でも k += 1 は k = k + 1 の短縮形なので、意味は分かりやすいです。
| 操作 | C++ | Python |
|---|---|---|
| 1増やす | k++ |
k += 1 |
| 1減らす | k-- |
k -= 1 |
| 3増やす | k += 3 |
k += 3 |
| 2倍にする | k *= 2 |
k *= 2 |
Pythonには ++ や -- がありません。そこは潔く諦めて、+= 1 に慣れるしかないです。
まとめ
振り返ると、入門編のつまずきはだいたいこのあたりに集まっていました。Pythonのルールがバラバラなのではなく、一貫したルールを知らなかっただけでした。
- 型は「見た目」ではなく「計算方法」で決まる
-
[]は「取り出す場所」という共通ルールがある - if は最終的に True / False だけを見ている
最初は「なんで?」「キモい」「分かりにくい」と思ったものばかりでした。でも理由を知ると、意外と納得できるものが多いです。
次は【発展編】です。クラスとオブジェクト、関数とメソッド、スコープ、importなど、もう一段抽象度が上がる疑問をまとめます。そのあとは、勉強中につまずいた「機械学習って結局ただの計算じゃないの?」という疑問についても書く予定です。こっちもかなり止まりました。