函数
把一段会重复使用的代码包起来,起个名字,就是函数。前面用过的 print、len、enumerate 都是 Python 自带的函数。这一节学自己写。
为什么需要函数
同一段打印写两遍,改格式时就要改两处:
print("*" * 5)
print("hello")
print("-" * 5)
print("*" * 5)
print("jeapedu")
print("-" * 5)
收成函数之后,格式只写一次。换内容时,只换调用时传进去的那个值。
定义和调用
def display(s):
print("*" * 5)
print(s)
print("-" * 5)
display("hello")
display("jeapedu")
def 是定义。display 是函数名,后面的括号和冒号不能少。缩进的几行是函数体,调用时才执行。定义本身不会打印任何东西。
括号里的 s 是参数,是函数内部使用的名字。调用时写进括号的 "hello" 是传进去的值,会赋给 s。
display("hello") 的结果:
*****
hello
-----
display("jeapedu") 的结果:
*****
jeapedu
-----
函数名后面必须有括号。只写 display 不会执行函数体。
返回值
print 只是把内容显示出来。函数还可以用 return 把结果交回去,调用的地方再用这个结果。
def add(x, y):
return x + y
print(add(1, 2))
3
return 一执行,函数就结束,后面的代码不会再跑。所以可以提前返回:
def label(score):
if score >= 60:
return "及格"
return "不及格"
print(label(75))
print(label(40))
及格
不及格
没有写 return 的函数,调用结果是 None。display 负责打印,并不交出一个值:
result = display("hello")
print(result)
*****
hello
-----
None
屏幕上有字,只说明函数里执行了 print。外面的 result 仍然是 None。要让外面拿到计算结果,必须 return。
return 后面可以一次写多个值,实际上是交回一个元组。接收时用 元组 那一节的解包:
def divide(a, b):
return a // b, a % b
print(divide(7, 2))
q, r = divide(7, 2)
print(q, r)
(3, 1)
3 1
a // b 是整除,a % b 是余数。7 除以 2 得到商 3、余 1。
多个参数和默认值
参数可以有好几个。没写默认值的,调用时必须传。写了默认值的,不传就用默认值。
def port(p=8080):
print(f"port = {p}")
port()
port(80)
port = 8080
port = 80
def host(ip, port=8080):
print(f"IP is {ip}:{port}")
host("127.0.0.1")
host("127.0.0.1", 80)
IP is 127.0.0.1:8080
IP is 127.0.0.1:80
ip 没有默认值,每次至少要传这一个。少传会报错:
host()
TypeError: host() missing 1 required positional argument: 'ip'
带默认值的参数要写在后面。下面这种定义是语法错误:
def host(port=8080, ip):
print(ip, port)
SyntaxError: parameter without a default follows parameter with a default
调用时也可以写上参数名,叫关键字参数。名字对上即可,顺序可以和定义时不同:
host("127.0.0.1", port=80)
host(port=80, ip="127.0.0.1")
两行都打印 IP is 127.0.0.1:80。参数一多,写出名字比只靠位置更不容易传错。
函数内外的名字
函数里新起的名字,只在函数里面存在。
def add(x, y):
total = x + y
return total
print(add(1, 2))
print(total)
3
NameError: name 'total' is not defined
参数也是函数自己的名字。外面有一个 n,函数里再写 n = n + 1,改的是里面的那一个,外面的数字不变:
n = 10
def change(n):
n = n + 1
return n
print(change(n))
print(n)
11
10
传进去的如果是列表,函数里用 append 改的是同一份列表,外面能看到:
nums = [1, 2]
def append_three(items):
items.append(3)
append_three(nums)
print(nums)
[1, 2, 3]
数字、字符串不能在原地修改,函数里的赋值只影响内部的名字。列表可以原地修改,传进去之后两边看到的是同一份。
参数个数不固定
参数前面加 *,多出来的位置参数会收成一个元组。一个都不传时,这个元组是空的。
def total(*nums):
result = 0
for n in nums:
result = result + n
return result
print(total(10, 20, 30))
print(total())
60
0
参数前面加 **,则把 名字=值 收成一个字典:
def show(**info):
print(info)
show(name="Jike", age=20)
{'name': 'Jike', 'age': 20}
入门先把普通参数、默认值和 return 写熟。读到别人代码里的 *args、**kwargs,就是这两种收集方式,名字可以自己起,* 和 ** 才是关键。
匿名函数:lambda
有些函数很简单,只用一次,专门起个名字反而麻烦。这种可以写成一行匿名函数 lambda。
lambda 的写法是 lambda 参数: 返回值。它没有 def、没有函数名,也没有 return 关键字——冒号后面的表达式结果就是返回值。
double = lambda x: x * 2
print(double(5))
10
和下面的普通函数效果一样,只是更短:
def double(x):
return x * 2
lambda 常和 map、filter、sorted 一起用。比如按某个条件过滤:
nums = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens)
[2, 4, 6]
filter 的第一个参数是"用来判断的函数",这里用 lambda x: x % 2 == 0 表示"x 能被 2 整除"。sorted 也可以用 key=lambda 指定按什么排:
words = ["apple", "banana", "pear"]
print(sorted(words, key=lambda w: len(w)))
['pear', 'apple', 'banana']
lambda 也能接收多个参数,例如按两个字段排序:
pairs = [("apple", 3), ("pear", 1), ("banana", 2)]
print(sorted(pairs, key=lambda p: p[1]))
[('pear', 1), ('banana', 2), ('apple', 3)]
什么时候用 lambda: 函数体只有一行表达式、又要作为参数传给别人时,用 lambda 最简洁。逻辑超过一行,或者要复用多次,还是用 def 起个名字更清楚。
常见坑
打印了,却没有返回。 函数里只有 print 时,外面接到的是 None,不能拿去继续计算。需要结果就写 return。
默认可变参数只创建一次。 默认值在定义函数时就算好,不是每次调用重新做一个新列表。
def add_item(item, bucket=[]):
bucket.append(item)
return bucket
print(add_item(1))
print(add_item(2))
[1]
[1, 2]
两次调用用的是同一个 bucket,所以 2 被追加进了已经有 1 的列表。需要“每次都是空列表”时,默认值写成 None,在函数里面再创建:
def add_item(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket
print(add_item(1))
print(add_item(2))
[1]
[2]
少传了没有默认值的参数。 报 TypeError,信息里会写出缺少的名字。
带默认值的参数写在了前面。 定义时就会 SyntaxError,函数还没机会被调用。
小结
def定义函数,调用时才执行函数体- 参数是函数内部的名字,调用时把值传进去
return把结果交回去,并立刻结束函数;不写return时结果是None- 一次
return多个值,得到的是元组 - 没默认值的参数放前面,调用时也可以用
名字=值 - 函数内部的新名字外面看不见;列表这类对象可以在函数里被原地修改
- 默认参数不要用
[]、{}这种会变的值 - 简单的一次性函数可以用
lambda 参数: 返回值,常配合map/filter/sorted使用
下一节学习 模块。用 python examples/functions.py 运行本节的例子。