函数
你已经用过不少函数了:print()、len()、sorted()。这一课自己写。函数是一段有名字的代码:写一次,以后要用的时候叫一声它的名字,它就跑一遍。
1def light_lamp():2 print("加满油")3 print("点着灯芯")4 5light_lamp()6light_lamp()
加满油 点着灯芯 加满油 点着灯芯
定义和调用
前三行是在定义函数。定义的时候不会跑,只是让 Python 认识一个新名字。写出函数名再加一对括号,叫调用,这时函数才真正跑起来。
| 部分 | 是什么 |
|---|---|
def | 「define」(定义)的缩写:从这里开始一个新函数 |
light_lamp | 函数名。规则和变量名一样。 |
(): | 一对括号,这一行最后有冒号 |
| 缩进的几行 | 函数体:每次调用都跑一遍 |
这段会显示什么?
1def say_hi():2 print("你好!")3 4print("开始")
def 只是定义函数。没有哪一行调用 say_hi(),所以函数体一次也不跑。
小技巧: 第一个例子点单步看看。每次调用,当前行都会跳进函数里,把函数体跑完,再回到调用的下一行。
参数
能交给函数一点东西让它处理,函数才更有用。在括号里写一个名字,这就是参数:一个在调用时拿到值的变量。调用时传进去的那个值叫实参。
1def light(street):2 print(f"点亮{street}的灯")3 4light("磨坊路")5light("石桥")
点亮磨坊路的灯 点亮石桥的灯
几个参数之间用逗号隔开。调用时按顺序一一对上:第一个实参给第一个参数,依此类推。少传一个,Python 就停下来报 TypeError,还会说少的是哪一个。
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() 的结果一样。
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?
只会 print 的函数能显示东西,但什么也不返回。没有 return 的函数返回 None。
显示出来的第二行是什么?
1def double(n):2 print(n * 2)3 4result = double(5)5print(result)
double(5) 自己显示了 10,但没有 return,所以返回 None。这个 None 存进 result,再被显示出来。
print(x) | return x | |
|---|---|---|
把 x 显示在屏幕上 | 会 | 不会 |
把 x 返回给调用的地方 | 不会 | 会 |
| 结束函数 | 不会 | 会 |
大多数函数应该 return 结果,让调用的人决定拿它做什么。这样同一个函数既能放进算式,也能放进 if,也能放进 print。
提前 return
return 会马上结束函数,哪怕在中间。函数里它后面的行都不跑了。所以可以先把特殊情况处理掉:
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 会同时结束循环和函数。下面这个函数找到第一盏没油的灯就返回:
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() 一样。
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(文档字符串),说明这个函数是干什么的。它不会显示任何东西,编辑器和各种工具会把它拿给用这个函数的人看。用三个引号 """ 可以写好几行。
函数可以调用别的函数。每个小函数只做一件事,再拼成大一点的函数:
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 那几行已经写好了。
灯的名字
✓写一个函数 label(n),返回 n 号灯 这样的文字。
1 号灯 12 号灯
1# 在这里写 label(n)2 3print(label(1))4print(label(12))
满了没有
✓把 cart_status(load) 写完:装了 10 块或更多返回 "满了",0 块返回 "空的",其他情况返回 "在装"。
满了 空的 在装
1def cart_status(load):2 return "在装"3 4print(cart_status(12))5print(cart_status(0))6print(cart_status(4))
总数和平均数
✓写 summary(loads),返回两个值:列表的总数和平均数。
18 6.0
1# 在这里写 summary(loads)2 3total, average = summary([4, 8, 6])4print(total, average)
这一课学会了
def 名字(参数):定义函数;只有名字(实参)调用时,函数体才跑。- 实参按顺序对上参数;少一个就是
TypeError。 return结束函数并返回一个值;没有return的函数返回None。print是显示;return是返回一个值,让调用的人接着用。return a, b返回一个能拆包的元组。docstring 说明函数是干什么的。