修虫小屋
恭喜你。你成为了 Crooked Orbit 的技术总监,这是一家小型、略显混乱的太空物流公司,由一群可疑的宇航员经营,他们的代码更加可疑。
薪水还不错。代码库就……
你的工作是处理这些问题:bug、坏掉的函数,以及那些技术上能运行但其实不该运行的东西。
#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?每次都这样。我把循环过了大概五遍,逻辑看起来没问题。分数明明就在那里。我不知道发生了什么 😅 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。这就是你打印出来的东西。
修复方法就是在最后加一行:
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 时,它在比较一个字符串和一个浮点数,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 不知道该怎么把它附加到周围的文本上。它选择拒绝而不是去猜。
修复方法是用 str() 把 weight 转换为字符串:
def cargo_label(item, weight):
label = "Item: " + item + " | Weight: " + str(weight) + "kg"
return label
print(cargo_label("Moon rocks", 42))
# Item: Moon rocks | Weight: 42kg对于这种情况,f-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("小明")
flight_2 = build_manifest("小红")
flight_3 = build_manifest("小华")
print(flight_1)
print(flight_2)
print(flight_3)期望:
['小明']
['小红']
['小华']实际:
['小明']
['小明', '小红']
['小明', '小红', '小华']哪里出错了?该怎么修复?
提示
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("小明")
flight_2 = build_manifest("小红")
flight_3 = build_manifest("小华")
print(flight_1) # ['小明']
print(flight_2) # ['小红']
print(flight_3) # ['小华']现在每次不传入列表的调用都会得到自己全新的列表。
这适用于任何可变默认值:列表、字典、集合。如果你希望每次都得到一个新的,就不要把它放在函数签名里。用 None,然后在函数内部创建。

