深色模式
第 04 章 · 函数:把代码打包成工具
本章目标
会定义函数、传参数、拿返回值,理解作用域,写出可复用、可读的代码。
4.1 为什么要函数
不用函数时,同样三行代码要抄三遍;抄错一处就出 bug。函数就是给一段逻辑起个名字,以后叫名字就行。
python
def greet(name):
"""打招呼(这是文档字符串 docstring)"""
print(f"你好,{name}!")
greet("小明")
greet("小红")def开头,函数名后跟括号和冒号- 函数体缩进
- 调用时才执行,定义时不执行
4.2 参数与返回值
python
def add(a, b):
return a + b
result = add(1, 2)
print(result) # 3return 做两件事:返回值 + 立刻结束函数。
python
def check(n):
if n < 0:
return "负数" # 到这里就结束了
return "非负数"
print(check(-5))没有 return(或只写 return)时,函数返回 None:
python
def show():
print("只做事,不返回")
print(show()) # 先打印"只做事,不返回",再打印 None4.3 参数的各种写法
默认值参数
python
def greet(name, greeting="你好"):
return f"{greeting},{name}"
print(greet("小明")) # 你好,小明
print(greet("Tom", "Hello")) # Hello,Tom⚠️ 默认值别用可变对象(列表/字典),会被"记住":
python
def bad(item, box=[]): # ❌ 不要这样写
box.append(item)
return box
print(bad(1)) # [1]
print(bad(2)) # [1, 2] ← 以为会重新开始,结果攒起来了
def good(item, box=None): # ✅ 正确写法
if box is None:
box = []
box.append(item)
return box关键字参数
调用时写参数名,顺序就随便了,代码更清楚:
python
def create_user(name, age, city):
return f"{name}/{age}/{city}"
print(create_user(city="鄂尔多斯", name="Rick", age=30))可变参数 *args / **kwargs
python
def total(*nums): # 收成元组
return sum(nums)
print(total(1, 2, 3)) # 6
def show(**info): # 收成字典
for k, v in info.items():
print(k, "=", v)
show(name="小明", age=18)调用时的 * 反过来用是解包:
python
nums = [1, 2, 3]
print(total(*nums)) # 等价于 total(1, 2, 3)特殊参数 / 和 *(选读,写工具/库时再看)
看到 def f(a, /, b, *, c) 别慌,意思是:
/前面的只能用位置传:f(1, b=2, c=3)✅,f(a=1, b=2, c=3)❌*后面的只能用关键字传:c=3必须写名字,f(1, 2, 3)❌
什么时候真的需要?当你写一个给别人用的函数,想明确禁止某种传参方式时。比如关键开关参数强制写名字,读代码的人一眼就知道它是什么:
python
CREATE_TABLE = True
def create_user(name, *, is_admin=False):
...
create_user("小明", is_admin=True) # ✅ 一眼看懂
# create_user("小明", True) # ❌ 报错,挡住歧义自己写小脚本时用不到,看到别人的代码里出现 / * 知道含义即可。
4.4 返回多个值
python
def min_max(nums):
return min(nums), max(nums) # 其实返回一个元组
low, high = min_max([3, 8, 1])
print(low, high) # 1 84.5 lambda 与函数当作参数
lambda 是"一次性小函数":
python
square = lambda x: x * x
print(square(5)) # 25
# 常见用途:排序时指定按什么排
students = [("小明", 92), ("小红", 88), ("小刚", 95)]
students.sort(key=lambda s: s[1], reverse=True)
print(students) # [('小刚', 95), ('小明', 92), ('小红', 88)]4.6 作用域:变量在哪儿有效
python
count = 0 # 全局变量
def add_one():
global count # 声明"我要改外面那个 count"
count += 1
add_one()
print(count) # 1规则很简单:函数里能读外面的变量,但想改外面的变量必须 global(实际写代码时尽量避免,靠参数传入、return 传出更清楚)。
python
def f():
x = 10 # 局部变量,函数外访问不到
f()
# print(x) # ❌ NameError4.7 文档字符串与类型注解
python
def area(width: float, height: float) -> float:
"""计算矩形面积。
width: 宽
height: 高
返回:面积
"""
return width * height注解只是给人看的说明,不会强制检查,但编辑器能帮你提示错误。养成写类型注解的习惯,代码质量立刻上一个档次。
4.8 函数的递归(进阶了解)
函数自己调用自己,必须有"出口":
python
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120递归能用循环替代,多数场景循环更直观。
4.9 编码风格三条(PEP 8)
- 函数名用
小写_下划线,望文知义(calc_tax而不是f2) - 一个函数只干一件事;超过 30 行就该考虑拆开
- 运算符两边加空格:
a = b + c
4.10 练一练
- 写
is_prime(n)判断质数,并打印 1–50 中的所有质数。 - 写
calc(nums)返回列表的平均值、最大值、最小值三个值。 - 写
safe_int(s, default=0):能转成整数就返回整数,否则返回默认值(提示:用try/except,见第 8 章;也可以先试着用字符串方法判断)。 - 把第 03 章的猜数字游戏改造成函数版本:
guess_game(answer)。
本章关键词
def、return、默认参数、*args / **kwargs、lambda、作用域 global、docstring、类型注解