バグハウス
おめでとうございます。あなたは Crooked Orbit の技術責任者です。怪しい宇宙飛行士たちが運営している、ちょっと混沌とした小さな宇宙物流会社で、彼らが書くコードはさらに怪しいものばかり。
給料はまあまあ。でもコードベースはそうでもない…
あなたの仕事は、バグ、壊れた関数、技術的には動くけど本当は動くべきじゃないものなど、問題を一つずつ片付けていくことです。
#1: 名前フォーマッター
.capitalize() を呼んでるのが見えるんだけど。意味わかんない。ちょっと見てくれない? def format_name(full_name):
parts = full_name.split()
for part in parts:
part = part.capitalize()
return " ".join(parts)
print(format_name("alice van den berg"))期待される結果: Alice Van Den Berg
実際の結果: alice van den berg
何が問題で、どう直しますか?
ヒント
for ループの中で変数を再代入したとき、実際には何が起こるか考えてみてください。part を変更すると、それが元のリストに影響しますか?
修正方法の一例
for part in parts と書くと、Python はリストから一度に一つずつ要素を取り出してくれます。part は現在の要素を保持している一時的な変数にすぎません。リストそのものへの直接的なつながりではありません。
つまり part = part.capitalize() と書くと、part が指している先を置き換えているだけです。parts には元の文字列がそのまま入っています。実際にはリストを変更していないのです。
これをはっきりと見るには:
parts = ["alice", "van", "den", "berg"]
for part in parts:
part = part.capitalize()
print(parts)
# ['alice', 'van', 'den', 'berg'] # 何も変わっていない修正するには、大文字化された値で新しいリストを作る必要があります。リスト内包表記がこれをきれいに行えます:
def format_name(full_name):
parts = full_name.split()
return " ".join(part.capitalize() for part in parts)
print(format_name("alice van den berg"))
# Alice Van Den Bergpart.capitalize() for part in parts は各名前を順に処理して、大文字化し、結果を新しいリストに集めます。そして .join() でそれらをスペースで繋ぎ直します。
#2: トップスコア
None って出力されるだけなの。毎回。もう5回くらいループを見直したけど、ロジックは大丈夫そうに見えるんだよね。スコアはちゃんと入ってるし。何が起こってるのかわからない 😅 def highest_score(scores):
best = 0
for score in scores:
if score > best:
best = score
results = [45, 92, 78, 88, 65]
print(f"Top score: {highest_score(results)}")期待される結果: Top score: 92
実際の結果: Top score: None
何が問題で、どう直しますか?
ヒント
return 文を書かなかった場合、関数は何を返しますか?
修正方法の一例
ループは問題ありません。ロジックも問題ありません。問題は、highest_score が実際には何も返していないことです。最高スコアを見つけて、そのまま何もしません。
Python では、関数に return 文がない場合、自動的に None を返します。それが出力されているものです。
修正は最後に1行追加するだけ:
def highest_score(scores):
best = 0
for score in scores:
if score > best:
best = score
return best
results = [45, 92, 78, 88, 65]
print(f"Top score: {highest_score(results)}")
# Top score: 92これはよくあることです。ロジックは全部そこにあるので関数は完成しているように見えますが、return がないと、関数が終わったときに結果が消えてしまいます。
#3: 予算チェック
def check_budget(threshold):
spent = input("How much have you spent? ")
if spent >= threshold:
print("Over budget!")
else:
print("You're within budget.")
check_budget(500.0)エラー:
TypeError: '>=' not supported between instances of 'str' and 'float'何が問題で、どう直しますか?
ヒント
ユーザーが何を入力しても、input() は常にどんな型を返しますか?
修正方法の一例
input() は常に文字列を返します。ユーザーが数字を入力したかどうかは関係ありません。Python にはそれがわかりません。入力されたものをそのままテキストとして返すだけです。
なので spent >= threshold を比較しようとすると、文字列と float を比較していることになり、Python はそれを拒否します。これが TypeError の正体です。
修正は、比較する前に入力を数値に変換することです。小数を扱うために float() を使います:
def check_budget(threshold):
spent = float(input("How much have you spent? "))
if spent >= threshold:
print("Over budget!")
else:
print("You're within budget.")
check_budget(500.0)
# How much have you spent? 620
# Over budget!input() を float() で包むと、すぐに文字列を数値に変換するので、比較が期待通りに動きます。
これは Python でよくある混乱の原因の一つです: input() は数字を読み取っているように見えますが、常に文字列を返します。自分で変換する必要があります。
#4: 貨物ラベル
def cargo_label(item, weight):
label = "Item: " + item + " | Weight: " + weight + "kg"
return label
print(cargo_label("Moon rocks", 42))エラー:
TypeError: can only concatenate str (not "int") to str何が問題で、どう直しますか?
ヒント
Python の + 演算子は数値を自動的に文字列に変換してくれません。結合する前に何をする必要がありますか?
修正方法の一例
Python で + を使って文字列を結合するときは、すべての値が文字列でなければなりません。weight は整数なので、Python はそれを周囲のテキストにどう繋げればよいかわかりません。推測せずに拒否します。
修正は、weight を str() で文字列に変換することです:
def cargo_label(item, weight):
label = "Item: " + item + " | Weight: " + str(weight) + "kg"
return label
print(cargo_label("Moon rocks", 42))
# Item: Moon rocks | Weight: 42kgf-string を使うと、こういう処理は変換を自動的に行ってくれるので、しばしばより簡潔になります:
def cargo_label(item, weight):
return f"Item: {item} | Weight: {weight}kg"
print(cargo_label("Moon rocks", 42))
# Item: Moon rocks | Weight: 42kg{} の中では、Python が自動的に値を文字列表現に変換するので、str() は不要です。
#5: 乗客名簿
def build_manifest(name, passengers=[]):
passengers.append(name)
return passengers
flight_1 = build_manifest("Pip")
flight_2 = build_manifest("Zee")
flight_3 = build_manifest("Orla")
print(flight_1)
print(flight_2)
print(flight_3)期待される結果:
['Pip']
['Zee']
['Orla']実際の結果:
['Pip']
['Pip', 'Zee']
['Pip', 'Zee', 'Orla']何が問題で、どう直しますか?
ヒント
Python のデフォルト引数の値は、関数が定義されたときに一度だけ評価されます。関数が呼び出されるたびにではありません。リストのようなミュータブルなオブジェクトにとって、これは何を意味するでしょうか?
修正方法の一例
これは Python の中でも最もよく知られている驚きの一つです。passengers=[] をデフォルト引数として書くと、Python は関数が定義されたときにそのリストを一度だけ作成します。デフォルトを使うすべての呼び出しが、まったく同じリストオブジェクトを共有しています。なので、ある呼び出しでそれに追加すると、その後のすべての呼び出しに影響します。
標準的な修正方法は、デフォルトとして None を使い、関数の中で新しいリストを作ることです:
def build_manifest(name, passengers=None):
if passengers is None:
passengers = []
passengers.append(name)
return passengers
flight_1 = build_manifest("Pip")
flight_2 = build_manifest("Zee")
flight_3 = build_manifest("Orla")
print(flight_1) # ['Pip']
print(flight_2) # ['Zee']
print(flight_3) # ['Orla']これでリストを渡さない呼び出しは、それぞれ真新しいリストを得るようになります。
これはあらゆるミュータブルなデフォルトに当てはまります: リスト、辞書、集合。毎回新しいものが欲しいなら、シグネチャに入れないでください。None を使って、関数の内側で作りましょう。

