📌 引言:不要重复发明轮子
还记得写九九乘法表的时候吗?你得把循环嵌套写一遍。要是哪天想改个格式,得把所有代码翻出来改。这就像每天做同样的饭,每次都要从头切菜——太累了!
函数就是编程界的“预制菜”。你把一段代码打包起来,起个名字,以后想用就“调用”一下。改了函数体,所有用到的地方都自动更新。这就是传说中的 DRY 原则(Don‘t Repeat Yourself)。
本章我们学习函数的方方面面,从定义到调用,从参数到作用域,从递归到 lambda。掌握函数,你的代码将脱胎换骨。
一、定义与调用:Hello Function!
1️⃣ 最简单的函数
def greet():
print("你好,世界!")
greet() # 调用函数
- def 关键字声明函数
- 函数名后跟圆括号和冒号
- 函数体缩进
- 调用时写函数名加括号
2️⃣ 带参数和返回值
def add(a, b):
result = a + b
return result
sum = add(3, 5)
print(sum) # 8
- 参数放在括号里,多个参数用逗号分隔
- return 返回结果,函数执行结束
- 如果没有 return,函数默认返回 None
3️⃣ 多个返回值(其实是元组)
def get_min_max(numbers):
return min(numbers), max(numbers)
result = get_min_max([1, 5, 3, 9, 2])
print(result) # (1, 9)
min_val, max_val = get_min_max([1, 5, 3, 9, 2]) # 解包
🧠 冷知识:函数是一等公民
在 Python 里,函数也是对象,可以赋值给变量、存在列表里、作为参数传递:
def say_hello():
print("Hello")
func = say_hello
func() # Hello
二、参数类型:Python 的花式传参
Python 的参数系统非常灵活,支持多种传递方式。
1️⃣ 位置参数(Positional Arguments)
最简单的形式,调用时按顺序赋值:
def describe_pet(name, species):
print(f"{name} 是一只 {species}")
describe_pet("旺财", "狗")
2️⃣ 默认参数(Default Arguments)
给参数指定默认值,调用时可以省略:
def describe_pet(name, species="狗"):
print(f"{name} 是一只 {species}")
describe_pet("旺财") # 旺财 是一只 狗
describe_pet("咪咪", "猫") # 咪咪 是一只 猫
⚠️ 重要陷阱:默认参数在函数定义时计算一次,如果默认值是可变对象(如列表、字典),多次调用会共享同一个对象!
def add_item(item, lst=[]): # 不要这样写!
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] → 咦?不是应该 [2] 吗?
正确做法:默认参数设为 None,内部再创建新对象。
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
3️⃣ 可变位置参数(*args)
当你不知道调用时会传入多少个位置参数时,用 *args。它会将多余的位置参数打包成元组。
def sum_all(*numbers):
total = 0
for n in numbers:
total += n
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(10, 20)) # 30
名字不一定要叫 args,但星号 * 是必须的,惯例用 *args。
4️⃣ 可变关键字参数(**kwargs)
处理任意数量的关键字参数,打包成字典。
def print_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="北京")
# 输出:
# name: Alice
# age: 25
# city: 北京
惯例用 **kwargs。
5️⃣ 组合参数顺序
如果同时使用多种参数,顺序必须固定:
位置参数 → *args → 默认参数 → **kwargs
def func(a, b, *args, c=10, **kwargs):
print(a, b, args, c, kwargs)
func(1, 2, 3, 4, 5, c=20, name="Alice")
# 1 2 (3, 4, 5) 20 {'name': 'Alice'}
6️⃣ 仅位置参数(Python 3.8+)
使用 / 分隔,/ 之前的参数只能按位置传递,不能作为关键字参数。
def power(x, y, /):
return x ** y
print(power(2, 3)) # 8
# print(power(x=2, y=3)) # 报错
7️⃣ 仅关键字参数(Keyword-Only Arguments)
使用 * 分隔,* 之后的参数必须用关键字传递。
def greet(name, *, greeting="你好"):
return f"{greeting},{name}"
print(greet("Alice")) # 你好,Alice
print(greet("Bob", greeting="Hello")) # Hello,Bob
# print(greet("Bob", "Hello")) # 报错
8️⃣ 解包参数调用(Unpacking Arguments)
调用函数时,可以用 * 解包序列,** 解包字典,将值传递给参数。
def add(a, b, c):
return a + b + c
nums = [1, 2, 3]
print(add(*nums)) # 6
params = {"a": 10, "b": 20, "c": 30}
print(add(**params)) # 60
三、作用域:变量的“地盘”
1️⃣ LEGB 规则
Python 查找变量时按照 LEGB 顺序:
- Local:函数内部局部作用域
- Enclosing:外层嵌套函数(闭包时用到)
- Global:全局作用域(模块级别)
- Built-in:内置作用域(如 print、len)
x = 10 # 全局
def outer():
y = 20 # 外层函数的局部
def inner():
z = 30 # 内层局部
print(x, y, z) # 都可以访问
inner()
outer()
2️⃣ global 声明
在函数内部修改全局变量,需要用 global 声明。
count = 0
def increment():
global count
count += 1
increment()
print(count) # 1
如果没有 global,函数内部的 count 会被当作局部变量,导致 UnboundLocalError。
3️⃣ nonlocal 声明
用于嵌套函数中修改外层函数的局部变量。
def outer():
x = 10
def inner():
nonlocal x
x += 5
print(x)
inner()
print(x) # 15
outer()
🧠 冷知识:Python 没有块级作用域
在 if、for、while 内部定义的变量,在外部依然可以访问(除非函数内部)。
if True:
msg = "Hello"
print(msg) # Hello
所以不要在循环内部滥用变量,以免意外覆盖。
四、递归函数:自己调用自己
递归就是函数调用自身。必须有两个要素:
- 基线条件(停止递归的条件)
- 递归条件(调用自身)
示例:阶乘
def factorial(n):
if n <= 1: # 基线条件
return 1
return n * factorial(n – 1) # 递归条件
print(factorial(5)) # 120
示例:斐波那契数列
def fib(n):
if n <= 1:
return n
return fib(n–1) + fib(n–2)
print(fib(10)) # 55
⚠️ 注意:Python 默认递归深度限制约 1000,可以用 sys.setrecursionlimit(2000) 调整,但不建议写太深的递归,容易栈溢出。
五、文档字符串(docstring):给自己留个便签
函数定义的第一行字符串(用三引号)就是文档字符串,用来描述函数功能、参数、返回值。
def add(a, b):
"""返回两个数的和"""
return a + b
print(add.__doc__) # 返回两个数的和
可以用 help(add) 查看文档。PEP 257 规定了文档字符串的格式。
六、匿名函数 lambda:轻量级小工具
当需要一个简单函数,又不想用 def 定义时,可以用 lambda。
square = lambda x: x ** 2
print(square(5)) # 25
语法:lambda 参数: 表达式,只能写一行,自动返回表达式结果。
通常和高阶函数一起用。
七、高阶函数简介(函数式编程启蒙)
高阶函数就是把函数作为参数或返回值的函数。
1️⃣ map():批量处理
map(func, iterable) 将函数作用于可迭代对象的每个元素,返回迭代器。
numbers = [1, 2, 3, 4]
squared = map(lambda x: x**2, numbers)
print(list(squared)) # [1, 4, 9, 16]
2️⃣ filter():筛选
filter(func, iterable) 返回使函数返回 True 的元素。
numbers = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens)) # [2, 4, 6]
3️⃣ sorted() 的 key 参数
sorted(iterable, key=func) 按照 key 函数的返回值排序。
students = [
{"name": "Alice", "grade": 88},
{"name": "Bob", "grade": 95},
{"name": "Charlie", "grade": 72},
]
sorted_by_grade = sorted(students, key=lambda s: s["grade"])
print(sorted_by_grade) # 按成绩升序
4️⃣ reduce()(了解即可)
functools.reduce(func, iterable) 将函数累积应用到元素上,返回单个值。
from functools import reduce
numbers = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 24
Python 3 中 reduce 移到了 functools,日常用得不多,但面试偶尔会问。
八、函数进阶预告
本章的函数基础篇没有涉及:
- 装饰器(动态增强函数)
- 生成器函数(yield)
- 闭包
这些将在第10章 高级特性 中展开。
✅ 本章总结
| 定义函数 | def + 函数名 + 参数 + 冒号 + 缩进体 |
| 返回值 | return,无 return 返回 None |
| 位置参数 | 按顺序赋值 |
| 默认参数 | param=value,注意可变默认值的坑 |
| 可变位置参数 | *args 收集多余位置参数为元组 |
| 可变关键字参数 | **kwargs 收集多余关键字参数为字典 |
| 仅位置参数 | / 之前参数不能作关键字 |
| 仅关键字参数 | * 之后参数必须用关键字 |
| 解包参数 | 调用时 *seq **dict |
| 作用域 | LEGB 规则 |
| global | 修改全局变量 |
| nonlocal | 修改外层嵌套变量 |
| 递归 | 自己调用自己,有基线条件 |
| 文档字符串 | 三引号写在函数开头 |
| lambda | 匿名函数,一行表达式 |
| 高阶函数 | map、filter、sorted key、reduce |
函数是模块化编程的基石。现在你可以把复杂任务拆解成一个个小函数,像搭积木一样构建程序。
🚀 下集预告
下一章我们将学习 模块与包——如何把函数组织成文件,如何导入别人写好的轮子,以及 Python 标准库的冰山一角。从此你的代码可以站在巨人的肩膀上!
记得多练习:写几个函数练习参数传递,试试递归解决汉诺塔问题,用 lambda 和 map/filter 处理数据。函数写多了,你会爱上这种“写一次,用一辈子”的感觉!✨
🔗 上一篇:流程控制:让程序学会思考与循环 🔗 下一篇:模块与包:站在巨人的肩膀上(待更新)
网硕互联帮助中心




评论前必须登录!
注册