【Python 基础全栈·第 04 篇】函数与作用域:参数·闭包·装饰器·Lambda——代码复用的艺术
【Python 基础全栈·第 04 篇】函数与作用域:参数·闭包·装饰器·Lambda——代码复用的艺术
系列回顾:第 01-02 篇搭建了环境、掌握了数据类型。第 03 篇学会了流程控制——if 选择、for/while 循环、推导式创建。本篇进入 Python 的"代码复用"核心——函数与作用域。函数是编程最基础的抽象机制——把一段逻辑封装成可复用的单元,通过参数传入数据,通过返回值传出结果。Python 的函数有三个核心特点:函数是一等公民(可以赋值、传参、返回)、参数极其灵活(位置/默认/可变/仅关键字)、闭包+装饰器(函数记住外层变量,动态增强函数行为)。本篇涵盖四大主题:函数定义与参数(5种参数类型/可变默认值陷阱/参数顺序规则)、作用域规则(LEGB 查找规则/global/nonlocal)、闭包与装饰器(闭包原理/装饰器模式/带参数装饰器/functools.wraps)、Lambda 与函数式编程(匿名函数/map/filter/sorted/key)。实战项目是任务管理器——一个支持增删查改、优先级、装饰器增强的命令行任务管理工具。

📑 文章目录
📦 一、函数定义与参数:5 种参数类型
1.1 函数定义基础
Python 用 def 关键字定义函数,用 return 返回结果。
def greet(name):
"""向指定用户打招呼"""
return f"Hello, {name}!"
# 调用函数
message = greet("Python")
print(message) # Hello, Python!
# 无返回值的函数(实际返回 None)
def say_hello(name):
print(f"Hello, {name}!")
result = say_hello("World") # Hello, World!
print(result) # None
关键要点:
- def 关键字:定义函数,后面跟函数名和参数列表
- docstring:三引号字符串,描述函数功能,可用
help(func)查看 - return:返回结果,可返回多个值(实际是元组),无 return 则返回 None
- 函数是一等公民:可以赋值给变量、作为参数传递、作为返回值
# 函数是一等公民
def add(a, b):
return a + b
# 赋值给变量
operation = add
print(operation(3, 5)) # 8
# 作为参数传递
def apply(func, x, y):
return func(x, y)
print(apply(add, 10, 20)) # 30
# 作为返回值
def get_operator(op):
if op == '+':
return add
# ...
my_add = get_operator('+')
print(my_add(1, 2)) # 3
1.2 五种参数类型
Python 函数支持 5 种参数类型,按严格顺序排列:
def func(pos1, pos2, # 1. 位置参数
key1=val1, key2=val2, # 2. 默认参数
*args, # 3. 可变位置参数
*, kw_only, # 4. 仅关键字参数
**kwargs): # 5. 可变关键字参数
pass
1. 位置参数(Positional Arguments)
最基础的参数类型,调用时按位置对应。
def power(base, exp):
return base ** exp
print(power(2, 10)) # 1024
2. 默认参数(Default Arguments)
参数有默认值,调用时可以省略。
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Python")) # Hello, Python!
print(greet("Python", "Hi")) # Hi, Python!
print(greet("Python", greeting="Hey")) # Hey, Python!
⚠️ 可变默认值陷阱——这是 Python 最著名的陷阱之一:
# ❌ 错误:可变默认值
def append_to(item, lst=[]):
lst.append(item)
return lst
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] ← 不是 [2]!默认值被共享了!
# ✅ 正确:用 None 替代
def append_to(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
print(append_to(1)) # [1]
print(append_to(2)) # [2] ← 正确!
原因:默认值在函数定义时创建一次,不是每次调用时创建。可变对象(list/dict/set)作为默认值会被所有调用共享。
*3. args(可变位置参数)
收集多余的位置参数为元组。
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
# 解包传入
numbers = [1, 2, 3, 4]
print(sum_all(*numbers)) # 10
4. 仅关键字参数(Keyword-Only Arguments)
在 *args 或单独的 * 之后的参数,必须用关键字传递。
# 用 * 强制关键字参数
def create_user(name, *, age, email):
return f"User: {name}, Age: {age}, Email: {email}"
# create_user("Alice", 25, "a@b.com") # ❌ TypeError
create_user("Alice", age=25, email="a@b.com") # ✅
# Python 3.8+ 用 / 强制位置参数
def greet(name, /, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Python") # ✅ 位置
greet("Python", "Hi") # ✅ 位置
greet("Python", greeting="Hi") # ✅ name只能位置
# greet(name="Python") # ❌ name是仅位置参数
**5. kwargs(可变关键字参数)
收集多余的关键字参数为字典。
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Python", version=3.12, creator="Guido")
# name: Python
# version: 3.12
# creator: Guido
# 解包传入
config = {"host": "localhost", "port": 8080}
print_info(**config)
1.3 参数顺序规则
Python 强制要求参数按以下顺序定义:
位置参数 → 默认参数 → *args → 仅关键字参数 → **kwargs
def func(a, b, # 位置参数
c=10, # 默认参数
*args, # 可变位置
d, # 仅关键字(在*args后)
e=20, # 仅关键字(有默认值)
**kwargs): # 可变关键字
print(f"a={a}, b={b}, c={c}")
print(f"args={args}")
print(f"d={d}, e={e}")
print(f"kwargs={kwargs}")
func(1, 2, 3, 4, 5, d=6, f=7, g=8)
# a=1, b=2, c=3
# args=(4, 5)
# d=6, e=20
# kwargs={'f': 7, 'g': 8}
🔭 二、作用域规则:LEGB·global·nonlocal
2.1 LEGB 规则
Python 用 LEGB 规则查找变量名:
| 层级 | 名称 | 说明 | 示例 |
|---|---|---|---|
| L | Local | 函数内部 | 函数内定义的变量 |
| E | Enclosing | 外层函数 | 闭包中的外层变量 |
| G | Global | 模块级 | 模块顶层定义的变量 |
| B | Built-in | 内置 | print/len/range 等 |
x = "global" # G: Global
def outer():
x = "enclosing" # E: Enclosing
def inner():
x = "local" # L: Local
print(x) # 查找顺序: L → E → G → B
inner()
outer() # local
查找规则:从内到外,找到第一个就停止。
# Built-in 层
print(len("hello")) # len 是 Built-in
# 如果被 Global 覆盖
len = "I'm not a function" # ⚠️ 覆盖了内置 len
# print(len("hello")) # ❌ TypeError
del len # 恢复内置
print(len("hello")) # 5
2.2 global 与 nonlocal
global:在函数内声明全局变量,允许修改。
counter = 0
def increment():
global counter # 声明使用全局变量
counter += 1
increment()
increment()
print(counter) # 2
nonlocal:在闭包内修改外层函数的变量。
def make_counter():
count = 0
def increment():
nonlocal count # 声明使用外层变量
count += 1
return count
return increment
c = make_counter()
print(c()) # 1
print(c()) # 2
print(c()) # 3
global vs nonlocal:
| 关键字 | 修改的层级 | 使用场景 |
|---|---|---|
| global | 修改 Global 层 | 模块级配置(少用) |
| nonlocal | 修改 Enclosing 层 | 闭包计数器/状态 |
🎭 三、闭包与装饰器:函数增强的终极模式
3.1 闭包(Closure)
闭包是一个函数对象,它记住了定义时所在作用域的变量,即使那个作用域已经不存在了。
def make_multiplier(factor):
"""工厂函数:创建乘法器"""
def multiply(x):
return x * factor # 记住了 factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
闭包的三要素:
- 有嵌套函数(内层函数)
- 内层函数引用外层变量
- 外层函数返回内层函数
闭包的应用:
# 1. 状态封装
def make_averager():
numbers = []
def add_number(n):
numbers.append(n)
return sum(numbers) / len(numbers)
return add_number
avg = make_averager()
print(avg(10)) # 10.0
print(avg(20)) # 15.0
print(avg(30)) # 20.0
# 2. 延迟计算
def lazy_property(func):
"""延迟计算属性"""
attr_name = '_lazy_' + func.__name__
@property
def wrapper(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, func(self))
return getattr(self, attr_name)
return wrapper
3.2 装饰器(Decorator)
装饰器是闭包的经典应用——在不修改原函数代码的情况下,动态增强函数行为。
import time
import functools
def timer(func):
"""计时装饰器:测量函数执行时间"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 执行耗时: {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "done"
slow_function() # slow_function 执行耗时: 1.0012s
装饰器的本质:
@timer
def func():
pass
# 等价于:
func = timer(func)
带参数的装饰器:
def retry(max_attempts=3, delay=1):
"""重试装饰器:失败时自动重试"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts:
raise
print(f"第{attempt}次失败: {e},{delay}秒后重试...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_api_call():
import random
if random.random() < 0.7:
raise ConnectionError("网络错误")
return "成功"
常用装饰器模式:
# 1. 日志装饰器
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
print(f"返回 {result}")
return result
return wrapper
# 2. 缓存装饰器
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
# 3. 权限装饰器
def require_auth(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated'):
raise PermissionError("需要登录")
return func(user, *args, **kwargs)
return wrapper
# 4. 类型检查装饰器
def typecheck(**expected_types):
def decorator(func):
@functools.wraps(func)
def wrapper(**kwargs):
for name, expected in expected_types.items():
if name in kwargs and not isinstance(kwargs[name], expected):
raise TypeError(f"{name} 应为 {expected}")
return func(**kwargs)
return wrapper
return decorator
functools.wraps 的重要性:
# ❌ 不用 wraps
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@bad_decorator
def my_function():
"""这是我的函数"""
pass
print(my_function.__name__) # wrapper ← 丢失了原函数名!
print(my_function.__doc__) # None ← 丢失了文档字符串!
# ✅ 用 wraps
def good_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@good_decorator
def my_function():
"""这是我的函数"""
pass
print(my_function.__name__) # my_function ← 保留了!
print(my_function.__doc__) # 这是我的函数 ← 保留了!
3.3 Lambda 表达式
Lambda 是一行匿名函数,语法:lambda 参数: 表达式
# 基本用法
square = lambda x: x ** 2
print(square(5)) # 25
add = lambda a, b: a + b
print(add(3, 5)) # 8
# 与 sorted 配合
students = [
{'name': 'Alice', 'score': 85},
{'name': 'Bob', 'score': 92},
{'name': 'Charlie', 'score': 78},
]
students_sorted = sorted(students, key=lambda s: s['score'], reverse=True)
# [{'name': 'Bob', 'score': 92}, {'name': 'Alice', 'score': 85}, ...]
# 与 map/filter 配合
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]
# 但推导式更 Pythonic!
squares = [x ** 2 for x in numbers] # 更好
evens = [x for x in numbers if x % 2 == 0] # 更好
Lambda 的使用原则:
- 简单逻辑用 Lambda:一行能写完的
- 复杂逻辑用 def:多行、有分支、有赋值的
- sorted/key 是最佳场景:排序的 key 函数
- 不要滥用:如果 Lambda 让代码更难读,就用 def
🛠️ 实战项目:任务管理器
综合运用函数、闭包、装饰器,实现一个命令行任务管理器:
"""
任务管理器 - 综合运用函数、闭包、装饰器
功能:增删查改、优先级、装饰器增强
"""
import functools
from datetime import datetime
# ===== 装饰器 =====
def log_action(func):
"""日志装饰器:记录操作"""
@functools.wraps(func)
def wrapper(manager, *args, **kwargs):
result = func(manager, *args, **kwargs)
action = func.__name__.replace('_', ' ')
print(f" 📋 操作: {action}")
return result
return wrapper
def validate_task_id(func):
"""验证任务ID装饰器"""
@functools.wraps(func)
def wrapper(manager, task_id, *args, **kwargs):
if task_id not in manager.tasks:
print(f" ❌ 任务 #{task_id} 不存在")
return None
return func(manager, task_id, *args, **kwargs)
return wrapper
# ===== 任务管理器 =====
def create_task_manager():
"""工厂函数:创建任务管理器(闭包)"""
tasks = {} # 任务存储
next_id = [1] # 用列表包装以便 nonlocal 修改
def add_task(title, priority="medium"):
"""添加任务"""
nonlocal next_id
task_id = next_id[0]
next_id[0] += 1
tasks[task_id] = {
'title': title,
'priority': priority,
'done': False,
'created': datetime.now().strftime('%Y-%m-%d %H:%M')
}
print(f" ✅ 任务 #{task_id} 已添加: {title} [{priority}]")
return task_id
@validate_task_id
def complete_task(task_id):
"""完成任务"""
tasks[task_id]['done'] = True
print(f" ✅ 任务 #{task_id} 已完成")
@validate_task_id
def delete_task(task_id):
"""删除任务"""
title = tasks[task_id]['title']
del tasks[task_id]
print(f" 🗑️ 任务 #{task_id} 已删除: {title}")
def list_tasks(filter_by=None):
"""列出任务"""
if not tasks:
print(" 📭 暂无任务")
return
priority_order = {'high': 0, 'medium': 1, 'low': 2}
sorted_tasks = sorted(
tasks.items(),
key=lambda x: (x[1]['done'], priority_order.get(x[1]['priority'], 1))
)
print("\n 📋 任务列表:")
print(" " + "-" * 50)
for tid, task in sorted_tasks:
status = "✅" if task['done'] else "⬜"
pri = {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(task['priority'], "⚪")
print(f" {status} #{tid}: {task['title']} {pri} ({task['created']})")
print(" " + "-" * 50)
def get_stats():
"""统计信息"""
total = len(tasks)
done = sum(1 for t in tasks.values() if t['done'])
return {'total': total, 'done': done, 'pending': total - done}
# 返回管理器对象(字典形式)
return {
'add': log_action(add_task),
'complete': log_action(complete_task),
'delete': log_action(delete_task),
'list': list_tasks,
'stats': get_stats,
'tasks': tasks
}
# ===== 主程序 =====
def main():
manager = create_task_manager()
# 添加任务
manager['add']("学习Python函数", "high")
manager['add']("完成项目报告", "high")
manager['add']("买咖啡", "low")
manager['add']("回复邮件", "medium")
# 列出任务
manager['list']()
# 完成任务
manager['complete'](1)
manager['complete'](3)
# 列出更新后的任务
manager['list']()
# 统计
stats = manager['stats']()
print(f"\n 📊 统计: 总计{stats['total']}个, 完成{stats['done']}个, 待办{stats['pending']}个")
if __name__ == '__main__':
main()
📌 第 04 篇总结
| 维度 | 核心内容 |
|---|---|
| 函数参数 | 5种参数类型 / 可变默认值陷阱 / 参数顺序规则 |
| 作用域 | LEGB查找规则 / global / nonlocal |
| 闭包与装饰器 | 闭包三要素 / 装饰器模式 / 带参数装饰器 / functools.wraps |
| Lambda | 匿名函数 / sorted+key / map+filter / 推导式更Pythonic |
| 实战项目 | 任务管理器——闭包+装饰器+优先级 |
三大要点:
- 参数顺序:位置→默认→*args→仅关键字→**kwargs,可变默认值用 None 替代
- LEGB 规则:从内到外查找变量,global 修改全局,nonlocal 修改外层
- 装饰器是函数增强的终极模式:不修改原函数,动态添加功能,functools.wraps 保留元信息
第 04 篇的核心 = 一等公民(基础) + 闭包(记忆) + 装饰器(增强) + Lambda(简洁),函数是代码复用的艺术
Python 基础全栈系列进度
| 篇号 | 主题 | 核心内容 | 状态 |
|---|---|---|---|
| 01 | 全景搭建 | 历史/安装/解释器/IDE/Pythonic | ✅ |
| 02 | 类型与运算 | 数字/字符串/布尔/类型转换/计算器 | ✅ |
| 03 | 流程控制 | if/for/while/推导式/猜数字 | ✅ |
| 04 | 函数与作用域(本文) | 参数/闭包/装饰器/Lambda/任务管理器 | ✅ |
| 05 | 数据结构 | 列表/元组/字典/集合/通讯录 | ⏳ 下一篇 |
| 06 | 面向对象 | 类/继承/多态/封装/魔术方法 | 待写 |
| 07 | 模块与包 | import/pip/虚拟环境/标准库 | 待写 |
| 08 | 文件与异常 | 读写/with/异常/上下文管理器 | 待写 |
| 09 | 高级特性 | 生成器/迭代器/描述符/元类 | 待写 |
| 10 | 实战工程 | 规范/测试/部署/项目结构 | 待写 |
下一篇预告:第 05 篇将深入数据结构——列表(list)/元组(tuple)/字典(dict)/集合(set)/推导式进阶/序列解包,实战项目是通讯录系统。
参考链接:
更多推荐
所有评论(0)