Kodewick
第 16 课,共 31 课目录

函数

第 16 课,共 31 课 · 第 3 部分 · 函数 · 约 20 分钟

你已经用过不少函数了:print()、len()、sorted()。这一课自己写。函数是一段有名字的代码:写一次,以后要用的时候叫一声它的名字,它就跑一遍。

main.py
1def light_lamp():2    print("加满油")3    print("点着灯芯")4 5light_lamp()6light_lamp()
输出
加满油
点着灯芯
加满油
点着灯芯

定义和调用

前三行是在定义函数。定义的时候不会跑,只是让 Python 认识一个新名字。写出函数名再加一对括号,叫调用,这时函数才真正跑起来。

部分是什么
def「define」(定义)的缩写:从这里开始一个新函数
light_lamp函数名。规则和变量名一样。
():一对括号,这一行最后有冒号
缩进的几行函数体:每次调用都跑一遍
先猜一猜

这段会显示什么?

1def say_hi():2    print("你好!")3 4print("开始")

小技巧: 第一个例子点单步看看。每次调用,当前行都会跳进函数里,把函数体跑完,再回到调用的下一行。

参数

能交给函数一点东西让它处理,函数才更有用。在括号里写一个名字,这就是参数:一个在调用时拿到值的变量。调用时传进去的那个值叫实参。

main.py
1def light(street):2    print(f"点亮{street}的灯")3 4light("磨坊路")5light("石桥")
输出
点亮磨坊路的灯
点亮石桥的灯

几个参数之间用逗号隔开。调用时按顺序一一对上:第一个实参给第一个参数,依此类推。少传一个,Python 就停下来报 TypeError,还会说少的是哪一个。

main.py
1def deliver(ore, count):2    print(f"送去 {count} 块{ore}")3 4deliver("铁矿", 12)5deliver("金矿")
输出
送去 12 块铁矿
Traceback (most recent call last):
  File "main.py", line 5, in <module>
    deliver("金矿")
TypeError: deliver() missing 1 required positional argument: 'count'

发生了什么: 函数要的参数个数不对,多给了或者少给了。

用 return 返回一个值

print 是把值显示在屏幕上。return 做的是另一件事:结束函数,把一个值交回调用它的地方。这个值可以存起来、显示出来、拿去算,就像 len() 的结果一样。

main.py
1def area(width, height):2    return width * height3 4room = area(4, 3)5print(room)6print(area(2, 5) + area(1, 1))
输出
12
11

这里点单步:调用一开始,width 和 height 作为函数自己的变量出现;return 一跑,它们就不见了,整个调用换成了它返回的值。

只会 print 的函数能显示东西,但什么也不返回。没有 return 的函数返回 None。

先猜一猜

显示出来的第二行是什么?

1def double(n):2    print(n * 2)3 4result = double(5)5print(result)
print(x)return x
把 x 显示在屏幕上会不会
把 x 返回给调用的地方不会会
结束函数不会会

大多数函数应该 return 结果,让调用的人决定拿它做什么。这样同一个函数既能放进算式,也能放进 if,也能放进 print。

提前 return

return 会马上结束函数,哪怕在中间。函数里它后面的行都不跑了。所以可以先把特殊情况处理掉:

main.py
1def ore_price(ore):2    if ore == "金":3        return 204    if ore == "铜":5        return 66    return 47 8print(ore_price("金"))9print(ore_price("铜"))10print(ore_price("煤"))
输出
20
6
4

循环里的 return 会同时结束循环和函数。下面这个函数找到第一盏没油的灯就返回:

main.py
1def first_empty(oil):2    for i, level in enumerate(oil):3        if level == 0:4            return i5    return -16 7print(first_empty([5, 3, 0, 7, 0]))8print(first_empty([5, 3]))
输出
2
-1

一次返回几个值

return 后面写几个值,用逗号隔开,函数就把它们放进一个元组返回。拆包以后每个值有自己的名字,和第 10 课的 divmod() 一样。

main.py
1def lowest_highest(numbers):2    return min(numbers), max(numbers)3 4print(lowest_highest([4, 9, 2, 7]))5low, high = lowest_highest([4, 9, 2, 7])6print(f"从 {low} 到 {high}")
输出
(2, 9)
从 2 到 9

docstring 和小帮手

写在函数体第一行的字符串叫 docstring(文档字符串),说明这个函数是干什么的。它不会显示任何东西,编辑器和各种工具会把它拿给用这个函数的人看。用三个引号 """ 可以写好几行。

函数可以调用别的函数。每个小函数只做一件事,再拼成大一点的函数:

main.py
1def area(width, height):2    """返回长方形的面积。"""3    return width * height4 5def floor_price(width, height):6    """返回给地板铺砖的价钱。78    每平方米的砖要 12 个金币。9    """10    return area(width, height) * 1211 12print(floor_price(4, 3))13print(floor_price(2, 2))
输出
144
48

点单步看调用一层套一层:floor_price 调用 area,area 把值返回给 floor_price,floor_price 再返回到 print 那一行。

为什么要写函数?

  • 只写一次。 有 bug 改一个地方就行,不用每份都改。
  • 起个名字。 floor_price(4, 3) 一看就知道在算什么,光看算式看不出来。
  • 拆成小块想。 每个函数都能单独试、单独看懂。

练一练

写好代码,点检查。提示随便看,实在想不出来再看答案。每个函数都要 return 结果,print 那几行已经写好了。

练习 1

灯的名字

写一个函数 label(n),返回 n 号灯 这样的文字。

要显示
1 号灯
12 号灯
exercise1.py
1# 在这里写 label(n)2 3print(label(1))4print(label(12))
输出
点「运行」或按 Ctrl+Enter
练习 2

满了没有

把 cart_status(load) 写完:装了 10 块或更多返回 "满了",0 块返回 "空的",其他情况返回 "在装"。

要显示
满了
空的
在装
exercise2.py
1def cart_status(load):2    return "在装"3 4print(cart_status(12))5print(cart_status(0))6print(cart_status(4))
输出
点「运行」或按 Ctrl+Enter
练习 3

总数和平均数

写 summary(loads),返回两个值:列表的总数和平均数。

要显示
18 6.0
exercise3.py
1# 在这里写 summary(loads)2 3total, average = summary([4, 8, 6])4print(total, average)
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • def 名字(参数): 定义函数;只有 名字(实参) 调用时,函数体才跑。
  • 实参按顺序对上参数;少一个就是 TypeError。
  • return 结束函数并返回一个值;没有 return 的函数返回 None。
  • print 是显示;return 是返回一个值,让调用的人接着用。
  • return a, b 返回一个能拆包的元组。docstring 说明函数是干什么的。