太棒了!欢迎来到 Python 30 天学习计划的第 10 天!🎉
今天我们将深入 函数进阶(Advanced Functions) —— 掌握让函数更灵活、更专业、更易维护的核心技巧。函数是 Python 的“积木块”,学会这些高级用法,你就能写出像标准库一样优雅、通用的代码!
🎯 第10天目标:
- ✅ 理解并正确使用默认参数(避免常见陷阱)
- ✅ 掌握关键字参数提升代码可读性
- ✅ 灵活运用
*args 和 **kwargs 实现可变参数函数
- ✅ 编写规范的 文档字符串(docstring),让代码自解释
📘 一、默认参数(Default Arguments)
默认参数为参数提供了“备用值”,在调用函数时可以省略。
🔹 基本用法
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hi")) # Hi, Bob!
⚠️ 经典陷阱:可变默认参数
# ❌ 危险!不要这样做!
def add_item(item, target_list=[]):
target_list.append(item)
return target_list
list1 = add_item("apple")
list2 = add_item("banana")
print(list1) # ['apple', 'banana'] ← 意外共享!
print(list2) # ['apple', 'banana']
原因:默认参数在函数定义时创建一次,后续所有调用都会共享同一个可变对象(列表)!
# ✅ 正确做法:用 None 作哨兵
def add_item(item, target_list=None):
if target_list is None:
target_list = []
target_list.append(item)
return target_list
list1 = add_item("apple")
list2 = add_item("banana")
print(list1) # ['apple']
print(list2) # ['banana']
🔑 黄金法则:永远不要用可变对象(list, dict, set)作为默认参数! 应使用 None 作为默认值,然后在函数体内部进行初始化。
📘 二、关键字参数(Keyword Arguments)
在调用函数时通过 参数名=值 的形式传递参数,这种方式可读性更高,并且传递顺序可以自由调整。
🔹 示例
def create_user(name, age, city="Unknown", active=True):
return {"name": name,
"age": age,
"city": city,
"active": active}
# 位置参数
user1 = create_user("Alice", 30)
# 混合使用(位置 + 关键字)
user2 = create_user("Bob", 25, city="New York")
# 全关键字(推荐用于多参数函数)
user3 = create_user(name="Charlie",
age=35,
active=False,
city="London")
print(user2)
# {'name': 'Bob', 'age': 25, 'city': 'New York', 'active': True}
🔹 强制关键字参数(Keyword-Only Arguments)
使用 * 作为分隔符,其后的参数在调用时必须以关键字形式传入。
def connect(host, port, *, timeout=5, secure=True):
print(f"Connecting to {host}:{port}, timeout={timeout}s, secure={secure}")
# connect("example.com", 443, 10) ❌ 报错!timeout 必须用关键字
# ✅ 正确用法
connect("example.com", 443, timeout=10)
✅ 用途:当函数参数较多、易混淆时,强制使用关键字参数能有效提高代码清晰度,避免传参错误。
📘 三、*args:接收任意数量的位置参数
*args 会将函数调用时传递的多余位置参数收集为一个元组。
🔹 示例:通用求和函数
def sum_all(*args):
print(f"Received args: {args}") # args 是元组
return sum(args)
print(sum_all(1, 2, 3))
# Received args: (1, 2, 3) → 6
print(sum_all(10, 20, 30, 40))
# Received args: (10, 20, 30, 40) → 100
🔹 拆包(Unpacking)反向使用
numbers = [1, 2, 3, 4]
print(sum_all(*numbers))
# 等价于 sum_all(1, 2, 3, 4)
💡 简单记忆:* 在函数定义时表示“收集”参数,在函数调用时表示“拆包”序列。
📘 四、**kwargs:接收任意数量的关键字参数
**kwargs 会将函数调用时传递的多余关键字参数收集为一个字典。
🔹 示例:创建灵活配置
def configure_app(name, **kwargs):
config = {"app_name": name}
config.update(kwargs) # 合并额外配置
return config
app = configure_app("MyApp", debug=True, port=8080, host="localhost")
print(app)
# 输出:{'app_name': 'MyApp', 'debug': True, 'port': 8080, 'host': 'localhost'}
🔹 拆包字典
settings = {"debug": True, "port": 3000}
app2 = configure_app("WebApp", **settings)
print(app2)
# 等价于 configure_app("WebApp”, debug=True, port=3000)
📘 五、组合使用:完整的函数签名
虽然 Python 支持复杂的参数顺序,但最常用且实用的模式是:
def flexible_func(required, default_val="default", *args, **kwargs):
print(f"Required: {required}")
print(f"Default: {default_val}")
print(f"Extra args: {args}")
print(f"Extra kwargs: {kwargs}")
flexible_func("hello", "world", 1, 2, 3, color="red", size="large")
输出:
Required: hello
Default: world
Extra args: (1, 2, 3)
Extra kwargs: {'color': 'red', 'size': 'large'}
✅ 这种参数模式在装饰器、Web 框架(如 Flask 路由)等需要高度灵活性的场景中被广泛使用!
📘 六、文档字符串(Docstring)—— 让函数会说话
用 三重引号 编写的注释,用于描述函数用途、参数、返回值,是实现代码自解释的关键。
🔹 Google 风格(推荐)
def calculate_tax(income, rate=0.1):
"""计算个人所得税。
Args:
income (float): 年收入,单位:元。
rate (float, optional): 税率。默认为 0.1 (10%)。
Returns:
float: 应缴税额。
Raises:
ValueError: 如果 income 为负数。
Examples:
>>> calculate_tax(100000)
10000.0
>>> calculate_tax(50000, rate=0.05)
2500.0
"""
if income < 0:
raise ValueError("收入不能为负数")
return income * rate
🔹 查看 docstring
help(calculate_tax) # 在 REPL 中查看
print(calculate_tax.__doc__) # 直接打印
✅ 好处:
- 自动生成 API 文档(Sphinx, pdoc)
- IDE 智能提示
- 极大提升团队协作与代码维护效率
💻 今日实战:构建一个灵活的日志记录函数
综合运用今日所学,我们来编写一个功能完善的日志记录函数。它应支持默认参数、可变参数(*details)和任意关键字参数(**metadata)。

✅ 今日小任务
*1、修改计算器函数,使其支持任意数量的数字相加/相乘(用 `args`)。**

2、思考题:为什么 def foo(a, b=10, *args, c) 会报错?
💡 答案:在 *args 之后的参数(如 c)必须是关键字参数,但这里 c 没有提供默认值,调用时必须显式指定 c=...,而函数定义时却允许它以位置参数形式出现,造成了歧义。如果想允许 c 以位置传参,应放在 *args 前;如果想强制 c 为关键字参数,应使用 * 分隔或提供默认值:
def foo(a, b=10, *, c): # c 必须关键字传入
pass
📝 小结
| 特性 |
作用 |
注意事项 |
| 默认参数 |
提供备用值,简化调用 |
避免可变对象(用 None 替代) |
| 关键字参数 |
提升可读性,传参顺序自由 |
参数较多时优先使用 |
| *`args`** |
接收任意数量的位置参数 |
收集为元组 |
| `kwargs`** |
接收任意数量的关键字参数 |
收集为字典 |
| 文档字符串 |
实现代码自文档化 |
遵循 Google/NumPy 等规范风格 |
✅ 最佳实践:
- 默认参数使用不可变对象。
- 多参数函数优先使用关键字参数调用。
- 所有公开的函数和方法都应撰写清晰的 docstring,这是优秀的技术文档习惯。
*args/**kwargs 用于需要极致灵活性的场景(如装饰器、API 封装)。
🎉 恭喜你完成第 10 天的学习!
你已经掌握了 编写专业级 Python 函数的核心技能,代码正变得越来越优雅!✨ 不妨将你的理解和实践代码记录下来,在云栈社区分享你的学习笔记,与其他开发者一起交流成长。