Python 函数
函数是组织代码的基本单位——把一段逻辑封装起来、起个名字、需要时调用,避免重复,提升可读性。Python 的函数非常灵活:支持默认参数、可变参数、关键字参数、多返回值、闭包、装饰器。这一章把这些全讲清楚。
1. 定义与调用
# 定义函数:def + 函数名 + (参数列表) + 冒号
def greet(name):
"""向某人打招呼(这是文档字符串 docstring)"""
return f"Hello, {name}!"
# 调用函数
message = greet("小明")
print(message) # Hello, 小明!
# 没有 return 的函数,默认返回 None
def say_hi(name):
print(f"Hi, {name}")
result = say_hi("小红") # 输出: Hi, 小红
print(result) # None
# 用 help() 查看 docstring
help(greet)
# 输出:
# Help on function greet:
# greet(name)
# 向某人打招呼(这是文档字符串 docstring)docstring(文档字符串)是 Python 函数的"自带说明书",用三引号紧贴 def 后面。养成写 docstring 的习惯,代码立刻提升一个档次——用 help(函数名) 就能看到。
2. 参数:位置、关键字、默认值
# 1. 位置参数:按顺序传
def add(a, b):
return a + b
print(add(2, 3)) # 5
# 2. 关键字参数:按名字传,顺序随意
def greet(name, greeting):
return f"{greeting}, {name}!"
print(greet(greeting="早上好", name="小明")) # 早上好,小明!
# 3. 默认参数:有默认值,调用时可省略
def greet(name, greeting="你好"):
return f"{greeting}, {name}!"
print(greet("小明")) # 你好,小明!
print(greet("小红", "嗨")) # 嗨,小红!
# ⚠ 默认参数陷阱:不要用可变对象(列表/字典)做默认值!
def buggy(item, lst=[]): # ❌ 危险!所有调用共享同一个列表
lst.append(item)
return lst
print(buggy(1)) # [1]
print(buggy(2)) # [1, 2] <- 不是 [2]!lst 被复用了
# ✅ 正确写法:用 None 做占位
def safe(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
print(safe(1)) # [1]
print(safe(2)) # [2] 每次都是新列表默认参数陷阱是 Python 经典面试题:可变对象(列表/字典)做默认值会在所有调用间共享。记住解法——用 None 占位,函数内创建。
3. 可变参数 *args / **kwargs
# 可变参数:*args 收集多余位置参数成元组
def sum_all(*args):
print(type(args)) # <class 'tuple'>
return sum(args)
print(sum_all(1, 2, 3, 4, 5)) # 15
# **kwargs 收集多余关键字参数成字典
def show_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
show_info(name="小明", age=20, city="北京")
# 输出:
# name: 小明
# age: 20
# city: 北京
# 混合使用:位置参数 + *args + **kwargs
def make_profile(name, *hobbies, **details):
print(f"姓名:{name}")
print(f"爱好:{hobbies}")
print(f"其他:{details}")
make_profile("小明", "读书", "编程", age=20, city="北京")
# 姓名:小明
# 爱好:('读书', '编程')
# 其他:{'age': 20, 'city': '北京'}*args 收集多余的位置参数成元组,**kwargs 收集多余的关键字参数成字典。这种"万能参数"在写通用工具、装饰器、框架时极其常用。
4. 返回多个值
# Python 函数可以"返回多个值"(实际是返回元组)
def min_max_avg(numbers):
return min(numbers), max(numbers), sum(numbers) / len(numbers)
low, high, avg = min_max_avg([3, 1, 4, 1, 5, 9, 2, 6])
print(f"最小:{low},最大:{high},平均:{avg:.2f}")
# 输出: 最小:1,最大:9,平均:3.88本质是返回元组,调用方再解包。这是 Python 比 Java/C 优雅的地方(它们得定义结构体或对象)。
5. Lambda 匿名函数
# Lambda:匿名函数,一行写完的小函数
# 语法:lambda 参数: 表达式
# 等价于 def add(a, b): return a + b
add = lambda a, b: a + b
print(add(2, 3)) # 5
# Lambda 最常作为参数传给高阶函数
nums = [1, 2, 3, 4, 5]
# 配合 map:对每个元素做运算
squares = list(map(lambda x: x * x, nums))
print(squares) # [1, 4, 9, 16, 25]
# 配合 filter:过滤元素
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens) # [2, 4]
# 配合 sort 的 key
students = [("小明", 85), ("小红", 92), ("小刚", 78)]
students.sort(key=lambda s: s[1]) # 按分数排序
print(students)
# [('小刚', 78), ('小明', 85), ('小红', 92)]
# 复杂逻辑还是用 def 更清晰
# Lambda 限制:只能写一个表达式,不能有语句(if/for/赋值)Lambda 适合简短一次性的函数,常配合 map / filter / sort(key=) 使用。复杂逻辑还是用 def 更清晰——可读性更重要。
6. 作用域 LEGB
Python 查找变量名按 LEGB 顺序:Local(局部)→ Enclosing(嵌套)→ Global(全局)→ Built-in(内置)。
# 作用域 LEGB:Local → Enclosing → Global → Built-in
x = "全局变量" # Global
def outer():
y = "外层函数变量" # Enclosing(闭包场景)
def inner():
z = "内层变量" # Local
print(z) # 找 Local
print(y) # Local 没有,找 Enclosing
print(x) # 都没有,找 Global
print(len) # 都没有,找 Built-in(内置函数)
inner()
outer()
# global 关键字:在函数内修改全局变量
counter = 0
def increment():
global counter
counter += 1
increment()
print(counter) # 1
# nonlocal:修改外层(非全局)变量,用于闭包
def make_counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc7. 闭包
闭包 = 内层函数 + 它引用的外层变量。它的核心价值是"记住"创建时的环境,常用于工厂函数、装饰器、回调。
# 闭包:函数 + 它引用的外层变量
# 经典应用:工厂函数、装饰器
def make_multiplier(factor):
"""返回一个把输入乘以 factor 的函数"""
def multiply(n):
return n * factor
return multiply
# 创建不同的"乘法器"
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# 每个闭包都"记住"了自己创建时的 factor
print(double(20)) # 40
print(triple(20)) # 608. 装饰器(Python 高级特性)
装饰器是"接收函数、返回新函数"的高阶函数,用 @装饰器名 语法糖应用。能在不动原函数代码的前提下,给它加功能(计时、日志、权限检查、缓存)。
# 装饰器:在不动原函数代码的前提下,给它加功能
# 语法:@decorator_name
import time
def timing(func):
"""测量函数执行时间的装饰器"""
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs) # 调用原函数
elapsed = time.time() - start
print(f"{func.__name__} 耗时 {elapsed:.4f} 秒")
return result
return wrapper
@timing
def slow_sum(n):
return sum(range(n))
print(slow_sum(1000000))
# 输出:
# slow_sum 耗时 0.05xx 秒
# 499999500000
# @timing 等价于 slow_sum = timing(slow_sum)
# 装饰器本质就是"接收函数,返回新函数"的高阶函数9. 类型注解(Python 3.5+)
Python 是动态类型,但类型注解可以让你写出"看起来像静态类型"的代码,IDE 能据此做智能补全和类型检查。大项目强烈推荐用。
# 类型注解(Python 3.5+):给参数和返回值标类型
# 注意:Python 不会强制检查,只是"提示"给 IDE 和工具用
def add(a: int, b: int) -> int:
return a + b
def greet(name: str, times: int = 1) -> str:
return (f"Hello, {name}! ") * times
# 复杂类型用 typing 模块
from typing import List, Dict, Optional, Tuple
def process(items: List[int]) -> Dict[str, int]:
return {"sum": sum(items), "count": len(items)}
def find_user(uid: int) -> Optional[Dict]:
"""返回字典或 None"""
if uid == 1:
return {"name": "小明"}
return None
# 虽然不强制,但大项目强烈推荐写类型注解
# IDE(VS Code/PyCharm)能智能补全、提前发现类型错误小结
函数是组织代码的核心。重点掌握:def + docstring、位置/关键字/默认参数(避开可变默认值陷阱)、*args/**kwargs、多返回值、Lambda、LEGB 作用域、闭包、装饰器、类型注解。下一篇看模块与包——如何组织多个文件的项目。
← 上一篇 Python 条件与循环
下一篇 Python 模块与包 →