第33集:函数参数——默认参数

学习目标

  • 理解默认参数的概念
  • 掌握默认参数的定义和使用
  • 学会设置合适的默认值
  • 了解默认参数的注意事项

一、什么是默认参数?

默认参数是在定义函数时给参数指定默认值。调用时如果不提供该参数,就使用默认值。

核心特点

  • 简化调用:不需要每次都传递所有参数
  • 灵活使用:可以传递参数覆盖默认值
  • 向后兼容:添加新参数不影响现有调用
  • 减少重复:常用的值设为默认,避免重复输入

生活类比

  • 点餐时说"来杯咖啡":默认不加糖
  • 如果说"来杯咖啡,加糖":覆盖默认值
  • 默认参数就像"默认选项",可以修改

二、默认参数的语法

函数定义

def 函数名(参数1=默认值1, 参数2=默认值2, ...):
    函数体

调用方式

# 使用默认值
函数名()

# 覆盖默认值
函数名(新值1, 新值2)
定义 使用默认值调用 覆盖默认值调用
def func(a=1, b=2): func() func(10, 20)

三、基础示例

示例1:最简单的默认参数

def greet(name, message="你好!"):
    """向某人问候,默认消息是'你好!'"""
    print(f"{name},{message}")

# 使用默认消息
greet("小明")  # 输出:小明,你好!

# 覆盖默认消息
greet("小红", "早上好!")  # 输出:小红,早上好!

示例2:多个默认参数

def introduce(name, age=18, city="北京"):
    """自我介绍"""
    print(f"我叫{name},{age}岁,来自{city}")

# 使用所有默认值
introduce("小王")  # 输出:我叫小王,18岁,来自北京

# 覆盖部分默认值
introduce("小李", 25)  # 输出:我叫小李,25岁,来自北京

# 覆盖所有默认值
introduce("小张", 30, "上海")  # 输出:我叫小张,30岁,来自上海

示例3:带返回值的默认参数

def power(base, exponent=2):
    """计算幂,默认计算平方"""
    return base ** exponent

# 默认计算平方
print(power(5))  # 输出:25 (5²)

# 覆盖默认值,计算立方
print(power(5, 3))  # 输出:125 (5³)

四、混合使用位置参数和默认参数

示例4:必需参数和默认参数混合

def calculate_price(price, quantity=1, discount=0):
    """计算最终价格"""
    return price * quantity * (1 - discount)

# 只提供必需参数
result1 = calculate_price(100)
print(result1)  # 输出:100

# 提供必需参数和第一个默认参数
result2 = calculate_price(100, 3)
print(result2)  # 输出:300

# 提供所有参数
result3 = calculate_price(100, 3, 0.1)
print(result3)  # 输出:270

示例5:设置默认值的函数

def print_info(name, gender="女", age=20):
    """打印信息"""
    print(f"姓名:{name},性别:{gender},年龄:{age}")

# 调用示例
print_info("小美")  # 输出:姓名:小美,性别:女,年龄:20
print_info("小帅", "男")  # 输出:姓名:小帅,性别:男,年龄:20
print_info("老王", "男", 45)  # 输出:姓名:老王,性别:男,年龄:45

五、默认参数的常见应用

示例6:登录函数

def login(username, password, remember_me=False):
    """用户登录"""
    print(f"用户名:{username}")
    print(f"密码:{'*' * len(password)}")
    print(f"记住登录:{'是' if remember_me else '否'}")

# 不记住登录
login("user1", "123456")

# 记住登录
login("user2", "password123", remember_me=True)

示例7:发送邮件函数

def send_email(to, subject, body, priority="普通", cc=None):
    """发送邮件"""
    print(f"收件人:{to}")
    print(f"主题:{subject}")
    print(f"优先级:{priority}")
    if cc:
        print(f"抄送:{cc}")
    print(f"内容:{body[:20]}...")

# 普通邮件
send_email("test@example.com", "测试邮件", "这是一封测试邮件")

# 重要邮件
send_email("boss@example.com", "重要通知", "项目已上线", priority="紧急")

# 带抄送的邮件
send_email("team@example.com", "会议通知", "下午3点开会", cc="manager@example.com")

示例8:图形绘制函数

def draw_rectangle(width=10, height=5, char="*"):
    """绘制矩形"""
    for i in range(height):
        print(char * width)

# 默认矩形
print("默认矩形:")
draw_rectangle()

# 指定宽高
print("\n指定宽高的矩形:")
draw_rectangle(15, 8)

# 使用不同字符
print("\n使用#的矩形:")
draw_rectangle(12, 6, "#")

六、默认参数的重要规则

规则1:默认参数必须放在位置参数之后

# ❌ 错误:默认参数在前
def greet(name="你好", message):  # 报错
    print(f"{name},{message}")

# ✅ 正确:默认参数在后
def greet(message, name="你好"):
    print(f"{name},{message}")

规则2:默认参数只计算一次(重要!)

def append_item(item, item_list=[]):
    """追加元素到列表"""
    item_list.append(item)
    return item_list

# ⚠️ 注意:这可能导致意外行为
print(append_item(1))  # 输出:[1]
print(append_item(2))  # 输出:[1, 2](列表被累积)
print(append_item(3))  # 输出:[1, 2, 3](列表被累积)

规则3:使用None作为可变对象默认值

# ✅ 正确做法:使用None
def append_item(item, item_list=None):
    """追加元素到列表"""
    if item_list is None:
        item_list = []
    item_list.append(item)
    return item_list

# 现在每次都创建新列表
print(append_item(1))  # 输出:[1]
print(append_item(2))  # 输出:[2](新列表)
print(append_item(3))  # 输出:[3](新列表)

七、实际应用案例

案例1:配置函数

def create_table(rows, columns, width=1, height=1, border="solid"):
    """创建表格"""
    print(f"创建表格:{rows}行 × {columns}列")
    print(f"单元格宽:{width},高:{height}")
    print(f"边框样式:{border}")

# 不同配置的表格
print("简单表格:")
create_table(5, 3)

print("\n定制表格:")
create_table(10, 5, width=2, height=2, border="dashed")

案例2:数据分析函数

def analyze_data(data, show_mean=True, show_median=True, show_std=False):
    """数据分析"""
    n = len(data)
    mean = sum(data) / n
    sorted_data = sorted(data)
    median = sorted_data[n // 2] if n % 2 == 1 else (sorted_data[n//2-1] + sorted_data[n//2]) / 2
    std = (sum((x - mean) ** 2 for x in data) / n) ** 0.5
    
    print(f"\n数据:{data}")
    print(f"数量:{n}")
    
    if show_mean:
        print(f"均值:{mean:.2f}")
    if show_median:
        print(f"中位数:{median:.2f}")
    if show_std:
        print(f"标准差:{std:.2f}")

# 分析数据
analyze_data([1, 2, 3, 4, 5])
analyze_data([10, 20, 30, 40, 50, 60], show_std=True)
analyze_data([5, 10, 15], show_mean=False, show_median=False, show_std=True)

案例3:文件操作函数

def read_file(filename, encoding="utf-8", strip_newlines=True):
    """读取文件内容"""
    try:
        with open(filename, 'r', encoding=encoding) as f:
            content = f.read()
            if strip_newlines:
                content = content.strip()
        return content
    except FileNotFoundError:
        return f"文件 {filename} 不存在"
    except Exception as e:
        return f"读取错误:{e}"

# 使用不同的读取方式
print("使用默认参数读取:")
print(read_file("test.txt"))

print("\n保留换行符:")
print(read_file("test.txt", strip_newlines=False))

八、默认参数的最佳实践

实践1:合理设置默认值

# ✅ 好的默认值
def connect(host="localhost", port=8080, timeout=30):
    """连接服务器"""
    print(f"连接到 {host}:{port},超时{timeout}秒")

# 常见的默认值
connect()  # 使用所有默认值
connect("192.168.1.1")  # 只修改主机
connect(port=443)  # 只修改端口

实践2:布尔值作为默认参数

def save_data(data, overwrite=False, backup=True):
    """保存数据"""
    print(f"保存数据:{data}")
    print(f"覆盖模式:{'是' if overwrite else '否'}")
    print(f"创建备份:{'是' if backup else '否'}")

save_data("hello")  # 不覆盖,创建备份
save_data("world", overwrite=True)  # 覆盖,创建备份
save_data("test", backup=False)  # 不覆盖,不创建备份

实践3:枚举值作为默认参数

def sort_data(data, order="asc", algorithm="quick"):
    """排序数据"""
    print(f"排序方式:{order}")
    print(f"算法:{algorithm}")
    
    sorted_data = sorted(data, reverse=(order == "desc"))
    return sorted_data

# 使用不同的排序选项
result1 = sort_data([3, 1, 4, 1, 5, 9])  # 默认升序
result2 = sort_data([3, 1, 4, 1, 5, 9], order="desc")  # 降序

九、常见错误与调试

错误1:默认参数在位置参数之前

# ❌ 错误
def func(a=1, b):  # 报错
    pass

# ✅ 正确
def func(b, a=1):
    pass

错误2:可变对象默认值的问题

# ❌ 可能有问题
def add_item(item, items=[]):
    items.append(item)
    return items

# ✅ 推荐
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

调试技巧:打印参数值

def debug_func(a, b=10, c=20):
    """调试函数"""
    print(f"调试信息:a={a}, b={b}, c={c}")
    return a + b + c

# 调用时可以看到参数值
debug_func(5)
debug_func(5, 15)
debug_func(5, c=30)

十、默认参数的优势

优势 说明
简化调用 不需要传递所有参数
提高灵活性 可以覆盖默认值
向后兼容 添加新参数不影响现有代码
减少错误 常用值设为默认,减少输入错误
代码清晰 函数意图更明确

十一、小结

知识点 说明
默认参数 函数定义时指定的默认值
简化调用 可以不传递某些参数
覆盖值 可以传递新值覆盖默认值
位置要求 默认参数必须在位置参数之后
可变注意 避免使用可变对象作为默认值

十二、课后练习

练习1

定义函数power(base, exponent=2),计算幂,默认计算平方。

练习2

定义函数create_user(username, password, role="user", active=True),创建用户信息。

练习3

定义函数format_number(num, decimals=2, currency=False),格式化数字。

练习4

定义函数send_message(content, recipient, priority="normal"),发送消息。

练习5

定义函数calculate_rectangle_area(width=10, height=10),计算矩形面积,默认是正方形。

« 上一篇 函数参数-位置参数 下一篇 » 函数参数-关键字参数