Python 第十二天:*args、**kwargs 与默认参数

目录

  1. 函数参数基础回顾
  2. 默认参数详解
  3. *args 可变位置参数
  4. **kwargs 可变关键字参数
  5. 参数组合使用
  6. 参数解包与拆包
  7. 深入理解 * 的作用
  8. 强制关键字参数
  9. 参数注解与类型提示
  10. 实战练习

1. 函数参数基础回顾

位置参数

位置参数是最基本的参数类型,必须按照定义顺序传递:

def greet(name, age, city):
    """三个位置参数"""
    return f"我是{name},今年{age}岁,来自{city}"

# 必须按顺序传递
print(greet("张三", 25, "北京"))
# 输出: 我是张三,今年25岁,来自北京

# 错误示例:顺序不对
print(greet("北京", "张三", 25))  # 结果可能不符合预期

关键字参数

使用参数名传递,可以不按顺序:

def greet(name, age, city):
    return f"我是{name},今年{age}岁,来自{city}"

# 使用关键字参数,顺序可以任意
print(greet(age=25, city="北京", name="张三"))
# 输出: 我是张三,今年25岁,来自北京

# 混合使用:位置参数在前,关键字参数在后
print(greet("张三", city="北京", age=25))
# 输出: 我是张三,今年25岁,来自北京

2. 默认参数详解

基本语法

在函数定义时给参数指定默认值,使其成为可选参数:

def greet(name, greeting="你好", punctuation="!"):
    """默认参数示例"""
    return f"{greeting}{name}{punctuation}"

# 使用默认 greeting 和 punctuation
print(greet("张三"))              # 你好,张三!
print(greet("李四", "早上好"))    # 早上好,李四!
print(greet("王五", "晚上好", "。")) # 晚上好,王五。

默认参数的重要性

# 没有默认参数:每次都要提供所有参数
def connect_db(host, port, user, password, database):
    """连接数据库(无默认参数)"""
    return f"连接 {host}:{port}/{database} 用户={user}"

# 必须提供所有参数
conn1 = connect_db("localhost", 3306, "root", "123456", "mydb")

# 有默认参数:简化调用
def connect_db_default(host, port=3306, user="root", password="", database="test"):
    """连接数据库(带默认参数)"""
    return f"连接 {host}:{port}/{database} 用户={user}"

# 使用默认值
conn2 = connect_db_default("localhost")
conn3 = connect_db_default("localhost", user="admin")
conn4 = connect_db_default("localhost", database="production")

默认参数的常见陷阱

陷阱1:使用可变对象作为默认值

这是 Python 中最常见的错误之一!

# 错误示例:可变默认值被共享
def add_item_bad(item, items=[]):  # 永远不要这样做!
    items.append(item)
    return items

# 每次调用共享同一个列表
print(add_item_bad("apple"))   # ['apple']
print(add_item_bad("banana"))  # ['apple', 'banana']
print(add_item_bad("cherry"))  # ['apple', 'banana', 'cherry']

# 正确示例:使用 None 作为默认值
def add_item_good(item, items=None):  # 正确做法
    if items is None:
        items = []
    items.append(item)
    return items

# 每次调用创建新列表
print(add_item_good("apple"))   # ['apple']
print(add_item_good("banana"))  # ['banana']
print(add_item_good("cherry"))  # ['cherry']

陷阱2:默认参数在函数定义时求值

import datetime

# 错误示例:时间在函数定义时固定
def log_bad(msg, when=datetime.datetime.now()):
    return f"{when}: {msg}"

# 多次调用会得到相同的时间
print(log_bad("第一次"))
# 假设输出: 2024-01-01 10:00:00.123456: 第一次
print(log_bad("第二次"))  
# 输出: 2024-01-01 10:00:00.123456: 第二次(时间相同!)

# 正确示例:使用 None 并在函数内判断
def log_good(msg, when=None):
    if when is None:
        when = datetime.datetime.now()
    return f"{when}: {msg}"

# 每次调用得到当前时间
print(log_good("第一次"))
print(log_good("第二次"))

默认参数的最佳实践

# 最佳实践1:使用不可变类型作为默认值
def func1(path, mode="r"):
    """字符串是不可变类型,可以安全使用"""
    pass

# 最佳实践2:使用 None 作为可变默认值的默认值
def func2(data=None):
    if data is None:
        data = []
    data.append(1)
    return data

# 最佳实践3:对于需要保持状态的函数,显式传递
def process_items(initial_items):
    """显式传递初始列表"""
    items = initial_items.copy()  # 创建副本
    items.append("new")
    return items

my_items = ["a", "b"]
result = process_items(my_items)
print(my_items)  # ['a', 'b'] - 原列表未被修改

3. *args 可变位置参数

基本语法

*args 允许函数接受任意数量的位置参数,这些参数被收集到一个元组中:

def sum_all(*args):
    """求和函数,接受任意数量的参数"""
    print(f"接收到的参数类型: {type(args)}")
    print(f"参数内容: {args}")
    return sum(args)

# 调用方式
print(sum_all(1, 2, 3))        # 6
print(sum_all(1, 2, 3, 4, 5))   # 15
print(sum_all())                # 0

*args 的工作原理

def debug_args(*args):
    """展示 args 的本质"""
    print("=" * 40)
    print(f"参数个数: {len(args)}")
    for i, arg in enumerate(args):
        print(f"  参数{i}: {arg} (类型: {type(arg).__name__})")

debug_args(1, "hello", 3.14, True, [1, 2, 3])
# 输出:
# ========================================
# 参数个数: 5
#   参数0: 1 (类型: int)
#   参数1: hello (类型: str)
#   参数2: 3.14 (类型: float)
#   参数3: True (类型: bool)
#   参数4: [1, 2, 3] (类型: list)

使用 *args 实现灵活函数

# 示例1:计算任意数字的算术平均
def arithmetic_mean(*numbers):
    """计算算术平均数"""
    if not numbers:
        return None
    return sum(numbers) / len(numbers)

print(arithmetic_mean(10, 20, 30))           # 20.0
print(arithmetic_mean(5, 10, 15, 20, 25))    # 15.0

# 示例2:找出最大值
def find_max(*numbers):
    """找出一组数的最大值"""
    if not numbers:
        return None
    max_val = numbers[0]
    for num in numbers[1:]:
        if num > max_val:
            max_val = num
    return max_val

print(find_max(3, 7, 2, 9, 1))  # 9

# 示例3:字符串连接
def join_strings(*strings, separator=" "):
    """用分隔符连接字符串"""
    return separator.join(strings)

print(join_strings("Hello", "World", "Python"))
print(join_strings("apple", "banana", "cherry", separator=", "))

*args 与普通参数组合

def func(prefix, *args, suffix="!"):
    """前缀 + 可变参数 + 后缀"""
    result = prefix
    for arg in args:
        result += str(arg)
    result += suffix
    return result

print(func("数字:", 1, 2, 3, 4, 5))          # 数字:12345!
print(func("单词:", "a", "b", "c", suffix="?"))  # 单词:abc?

4. **kwargs 可变关键字参数

基本语法

**kwargs 允许函数接受任意数量的关键字参数,这些参数被收集到一个字典中:

def print_info(**kwargs):
    """打印所有关键字参数"""
    print(f"接收到的参数类型: {type(kwargs)}")
    print(f"参数内容: {kwargs}")
    for key, value in kwargs.items():
        print(f"  {key} = {value}")

# 调用方式
print_info(name="张三", age=25, city="北京")
print_info(a=1, b=2, c=3)

**kwargs 的工作原理

def debug_kwargs(**kwargs):
    """展示 kwargs 的本质"""
    print("=" * 40)
    print(f"参数个数: {len(kwargs)}")
    for key, value in kwargs.items():
        print(f"  {key}: {value} (类型: {type(value).__name__})")

debug_kwargs(name="Alice", score=95, is_active=True, hobbies=["reading", "coding"])
# 输出:
# ========================================
# 参数个数: 4
#   name: Alice (类型: str)
#   score: 95 (类型: int)
#   is_active: True (类型: bool)
#   hobbies: ['reading', 'coding'] (类型: list)

使用 **kwargs 实现灵活配置

# 示例1:创建用户配置
def create_user(name, **profile):
    """创建用户,可选添加任意配置项"""
    user = {"name": name}
    user.update(profile)
    return user

user1 = create_user("张三", age=25, city="北京", profession="工程师")
user2 = create_user("李四", email="li@example.com", phone="1234567890")
print(user1)
print(user2)

# 示例2:格式化日志消息
def log(level, message, **options):
    """日志函数,支持自定义格式"""
    timestamp = options.get("timestamp", "2024-01-01 12:00:00")
    prefix = options.get("prefix", "")
    
    full_message = f"[{timestamp}] [{level}]"
    if prefix:
        full_message += f" [{prefix}]"
    full_message += f" {message}"
    
    return full_message

print(log("INFO", "服务启动成功"))
print(log("ERROR", "连接失败", timestamp="2024-06-15 10:30:00", prefix="DB"))

**kwargs 与普通参数组合

def func(required_arg, *args, **kwargs):
    """参数组合示例"""
    print(f"必需参数: {required_arg}")
    if args:
        print(f"位置参数组: {args}")
    if kwargs:
        print(f"关键字参数组: {kwargs}")

func("test")  # 必需参数: test
func("test", 1, 2, 3)  # 必需参数: test, 位置参数组: (1, 2, 3)
func("test", 1, 2, 3, name="Alice", age=25)  # 全部参数

5. 参数组合使用

完整参数顺序

Python 函数参数的标准顺序:

def func(positional_only, /, positional_or_keyword, *args, keyword_only, **kwargs):
  • positional_only: 仅位置参数(/ 之前)
  • positional_or_keyword: 位置或关键字参数
  • *args: 可变位置参数
  • keyword_only: 仅关键字参数
  • **kwargs: 可变关键字参数
def complete_example(pos_only, /, standard, *args, kw_only, **kwargs):
    """
    完整参数示例
    - pos_only: 只能用位置传递
    - standard: 位置或关键字传递
    - args: 收集额外位置参数
    - kw_only: 必须用关键字传递
    - kwargs: 收集额外关键字参数
    """
    print(f"pos_only: {pos_only}")
    print(f"standard: {standard}")
    print(f"args: {args}")
    print(f"kw_only: {kw_only}")
    print(f"kwargs: {kwargs}")

# 调用
complete_example(1, 2, 3, 4, kw_only=5, extra="value")
# 输出:
# pos_only: 1
# standard: 2
# args: (3, 4)
# kw_only: 5
# kwargs: {'extra': 'value'}

常见组合模式

# 模式1:*args + **kwargs(最灵活)
def flexible(*args, **kwargs):
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

flexible(1, 2, 3, name="test", value=100)

# 模式2:默认参数 + *args + **kwargs
def with_defaults(a, b=10, *args, **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"kwargs={kwargs}")

with_defaults(1, 2, 3, 4, x=10, y=20)
# a=1, b=2, args=(3, 4), kwargs={'x': 10, 'y': 20}

# 模式3:默认值 + *args
def func3(a, b=20, *args):
    print(f"a={a}, b={b}, args={args}")

func3(1)        # a=1, b=20, args=()
func3(1, 2)     # a=1, b=2, args=()
func3(1, 2, 3)  # a=1, b=2, args=(3,)

实际应用示例

class Calculator:
    """计算器类,演示参数组合"""
    
    def calculate(self, operation, *numbers, precision=2, **options):
        """执行计算操作"""
        self._validate_numbers(*numbers)
        
        if operation == "sum":
            result = sum(numbers)
        elif operation == "avg":
            result = sum(numbers) / len(numbers) if numbers else 0
        elif operation == "max":
            result = max(numbers) if numbers else None
        elif operation == "min":
            result = min(numbers) if numbers else None
        else:
            raise ValueError(f"未知操作: {operation}")
        
        # 应用选项
        if options.get("round", True):
            result = round(result, precision)
        if options.get("negative"):
            result = -result
            
        return result
    
    def _validate_numbers(self, *numbers):
        """验证输入"""
        for num in numbers:
            if not isinstance(num, (int, float)):
                raise TypeError(f"所有参数必须是数字,得到: {type(num)}")


# 使用
calc = Calculator()
print(calc.calculate("sum", 1, 2, 3, 4, 5))              # 15
print(calc.calculate("avg", 10, 20, 30))                # 20.0
print(calc.calculate("max", 3, 7, 2, 9, 1))             # 9
print(calc.calculate("avg", 1, 2, 3, precision=1))       # 2.0
print(calc.calculate("sum", 1, 2, 3, negative=True))     # -6

6. 参数解包与拆包

在函数调用时使用 *

将列表或元组拆包为位置参数:

def add(a, b, c):
    """三个参数求和"""
    return a + b + c

# 普通调用
print(add(1, 2, 3))  # 6

# 使用 * 解包
numbers = [1, 2, 3]
print(add(*numbers))  # 6

# 解包与直接传参混用
print(add(*[1, 2], 3))  # 6

在函数调用时使用 **

将字典拆包为关键字参数:

def create_user(name, age, city):
    """创建用户"""
    return {"name": name, "age": age, "city": city}

# 普通调用
print(create_user("张三", 25, "北京"))

# 使用 ** 解包字典
config = {"name": "李四", "age": 30, "city": "上海"}
print(create_user(**config))

# 部分解包
print(create_user("王五", **{"age": 28, "city": "广州"}))

在函数定义时使用 *(强制收集)

def func(*args):
    """收集所有位置参数"""
    print(f"收集到: {args}")

# 调用时解包
func(*[1, 2, 3])  # 收集到: (1, 2, 3)
func(*[1, 2, 3], *[4, 5])  # 收集到: (1, 2, 3, 4, 5)

在函数定义时使用 **(强制收集)

def func(**kwargs):
    """收集所有关键字参数"""
    print(f"收集到: {kwargs}")

# 调用时解包
func(**{"a": 1, "b": 2})  # 收集到: {'a': 1, 'b': 2}
func(**{"x": 10}, **{"y": 20})  # 收集到: {'x': 10, 'y': 20}

综合示例

def configure(title, width=800, height=600, **options):
    """配置窗口"""
    config = {
        "title": title,
        "width": width,
        "height": height
    }
    config.update(options)
    return config

# 各种调用方式
print(configure("主窗口"))  # 基本调用
print(configure("设置", 1024, 768))  # 指定宽高
print(configure("游戏", height=1080, fullscreen=True, vsync=False))  # 关键字参数
print(configure(**{"title": "对话框", "width": 600}))  # 字典解包

7. 深入理解 * 的作用

* 的两个角色

# 角色1:在函数定义时 - 收集参数
def collect(*args):
    """收集位置参数"""
    print(args)

# 角色2:在函数调用时 - 展开参数
def show(a, b, c):
    print(f"a={a}, b={b}, c={c}")

numbers = [1, 2, 3]
show(*numbers)  # a=1, b=2, c=3

* 用在赋值中

# 解包赋值
a, *b, c = [1, 2, 3, 4, 5]
print(f"a={a}, b={b}, c={c}")  # a=1, b=[2, 3, 4], c=5

# 忽略不需要的值
head, *_, tail = [1, 2, 3, 4, 5]
print(f"head={head}, tail={tail}")  # head=1, tail=5

** 的两个角色

# 角色1:在函数定义时 - 收集关键字参数
def collect(**kwargs):
    print(kwargs)

# 角色2:在函数调用时 - 展开字典
def show(name, age):
    print(f"name={name}, age={age}")

info = {"name": "张三", "age": 25}
show(**info)  # name=张三, age=25

组合使用的完整示例

def demo(pos1, pos2, /, standard, *args, kw_only, **kwargs):
    """完整演示"""
    print(f"pos1={pos1}, pos2={pos2}")
    print(f"standard={standard}")
    print(f"args={args}")
    print(f"kw_only={kw_only}")
    print(f"kwargs={kwargs}")

# 各种调用方式
print("=" * 50)
print("方式1: 基本调用")
demo(1, 2, 3, kw_only=4)

print("=" * 50)
print("方式2: 使用 * 解包")
demo(*[1, 2], 3, kw_only=4, **{"extra": 5})

print("=" * 50)
print("方式3: 使用 ** 解包")
demo(1, 2, standard=3, kw_only=4, extra1=5, extra2=6)

8. 强制关键字参数

Python 3 的仅关键字参数

* 之后定义的参数必须使用关键字传递:

def func(a, b, *, c, d):
    """
    a, b: 可以用位置或关键字传递
    c, d: 必须用关键字传递
    """
    return f"a={a}, b={b}, c={c}, d={d}"

# 正确调用
print(func(1, 2, c=3, d=4))

# 错误调用 - c, d 不能用位置传递
# print(func(1, 2, 3, 4))  # TypeError

仅关键字参数的应用场景

# 场景1:防止参数顺序混淆
def create_user(name, /, *, age, email):
    """
    name: 仅位置(用户名)
    age, email: 仅关键字(必须指定参数名)
    """
    return {"name": name, "age": age, "email": email}

# name 可以直接传,其他必须用关键字
print(create_user("张三", age=25, email="zhang@example.com"))
print(create_user("李四", email="li@example.com", age=30))

# 场景2:增强代码可读性
def send_message(message, /, *, to, from_address, subject="", priority="normal"):
    """
    强制使用关键字参数,代码更清晰
    """
    return {
        "message": message,
        "to": to,
        "from": from_address,
        "subject": subject,
        "priority": priority
    }

msg = send_message("你好", to="user@example.com", from_address="system@company.com")

/ 和 * 的组合使用

def complex_func(
    pos_only,           # 位置限定: 必须用位置传参
    /,                  # 斜杠:左边仅位置
    pos_or_kw,          # 普通参数: 位置或关键字
    *,                  # 星号:右边仅关键字
    kw_only             # 关键字限定: 必须用关键字传参
):
    return f"{pos_only}, {pos_or_kw}, {kw_only}"

# 测试
print(complex_func(1, 2, kw_only=3))  # 正确
# print(complex_func(1, pos_or_kw=2, kw_only=3))  # 错误: pos_or_kw 不能用关键字
# print(complex_func(pos_only=1, 2, kw_only=3))  # 错误: pos_only 必须用位置

9. 参数注解与类型提示

基本类型注解

def greet(name: str, age: int) -> str:
    """带类型注解的函数"""
    return f"你好,{name}!你 {age} 岁了。"

print(greet("张三", 25))

*args 和 **kwargs 的类型注解

from typing import Tuple, Dict, Any

def func(*args: int, **kwargs: str) -> Tuple[tuple, dict]:
    """带类型的 *args 和 **kwargs"""
    return args, kwargs

# 调用
result = func(1, 2, 3, name="Alice", city="Beijing")
print(result)  # ((1, 2, 3), {'name': 'Alice', 'city': 'Beijing'})

Optional 和 Union 类型

from typing import Optional, Union, List

def process(
    data: List[int],
    multiplier: Optional[float] = None,
    **options: Union[str, int]
) -> Dict[str, Any]:
    """复杂类型注解示例"""
    result = sum(data)
    if multiplier is not None:
        result *= multiplier
    
    return {
        "sum": result,
        "count": len(data),
        "options": options
    }

print(process([1, 2, 3], multiplier=2.0, name="test"))

TypedDict(Python 3.8+)

from typing import TypedDict, List

class UserProfile(TypedDict):
    name: str
    age: int
    email: str
    hobbies: List[str]

def create_profile(**kwargs: str) -> UserProfile:
    """TypedDict 用法"""
    profile = UserProfile(
        name=kwargs.get("name", ""),
        age=int(kwargs.get("age", 0)),
        email=kwargs.get("email", ""),
        hobbies=kwargs.get("hobbies", [])
    )
    return profile

10. 实战练习

练习1:通用格式化函数

def format_table(
    *columns,
    delimiter: str = " | ",
    header: bool = True,
    align: str = "left",
    padding: int = 1
) -> str:
    """
    将多个列表格式化为表格字符串
    
    Args:
        *columns: 任意数量的列表,每个列表作为一列
        delimiter: 列分隔符
        header: 是否显示列标题
        align: 对齐方式 ('left', 'right', 'center')
        padding: 单元格内边距
    
    Returns:
        格式化的表格字符串
    """
    if not columns:
        return ""
    
    # 获取每列的最大宽度
    num_rows = max(len(col) for col in columns)
    col_widths = []
    
    for col in columns:
        col_widths.append(max(len(str(item)) for item in col) if col else 0)
    
    # 构建表格
    rows = []
    padding_str = " " * padding
    
    def format_cell(content, width):
        content = str(content)
        pad = padding_str
        if align == "left":
            return f"{pad}{content:<{width}}{pad}"
        elif align == "right":
            return f"{pad}{content:>{width}}{pad}"
        else:  # center
            return f"{pad}{content:^{width}}{pad}"
    
    # 添加表头行
    if header:
        header_row = delimiter.join(
            format_cell(f"列{i+1}", col_widths[i]) 
            for i in range(len(columns))
        )
        rows.append(header_row)
        # 分隔线
        separator = delimiter.join("-" * (w + 2 * padding) for w in col_widths)
        rows.append(separator)
    
    # 添加数据行
    for row_idx in range(num_rows):
        row_values = []
        for col_idx, col in enumerate(columns):
            value = col[row_idx] if row_idx < len(col) else ""
            row_values.append(format_cell(value, col_widths[col_idx]))
        rows.append(delimiter.join(row_values))
    
    return "\n".join(rows)


# 测试
names = ["Alice", "Bob", "Charlie", "David"]
ages = [25, 30, 35, 28]
cities = ["Beijing", "Shanghai", "Guangzhou", "Shenzhen"]

print(format_table(names, ages, cities))
print("\n" + "=" * 50 + "\n")
print(format_table(names, ages, cities, header=False))
print("\n" + "=" * 50 + "\n")
print(format_table(names, ages, cities, align="right", padding=2))

练习2:函数装饰器(使用 *args, **kwargs)

from functools import wraps
from typing import Callable, Any
import time

def timing(func: Callable) -> Callable:
    """测量函数执行时间的装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"函数 {func.__name__} 执行时间: {end - start:.4f} 秒")
        return result
    return wrapper

def validate_args(**validators):
    """验证参数类型的装饰器工厂"""
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 获取函数签名
            import inspect
            sig = inspect.signature(func)
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            
            # 验证参数
            for name, validator in validators.items():
                if name in bound.arguments:
                    value = bound.arguments[name]
                    if not validator(value):
                        raise ValueError(f"参数 {name}={value} 验证失败")
            
            return func(*args, **kwargs)
        return wrapper
    return decorator

@timing
@validate_args(age=lambda x: 0 < x < 150, name=lambda x: isinstance(x, str) and len(x) > 0)
def create_person(name: str, age: int, city: str = "北京") -> dict:
    """创建人物信息"""
    return {"name": name, "age": age, "city": city}

# 测试
print(create_person("张三", 25))
print(create_person("李四", age=30))
# create_person("", 25)  # 会抛出异常
# create_person("王五", 200)  # 会抛出异常

练习3:链式函数调用

class QueryBuilder:
    """SQL 查询构建器,演示参数传递"""
    
    def __init__(self, table: str):
        self.table = table
        self._fields = ["*"]
        self._conditions = []
        self._order_by = None
        self._limit_val = None
    
    def select(self, *fields: str) -> "QueryBuilder":
        """选择字段"""
        if fields:
            self._fields = list(fields)
        return self
    
    def where(self, condition: str, *params) -> "QueryBuilder":
        """添加 WHERE 条件"""
        self._conditions.append((condition, params))
        return self
    
    def order_by(self, field: str, direction: str = "ASC") -> "QueryBuilder":
        """排序"""
        self._order_by = (field, direction.upper())
        return self
    
    def limit(self, count: int, offset: int = 0) -> "QueryBuilder":
        """限制结果数"""
        self._limit_val = (count, offset)
        return self
    
    def build(self) -> tuple:
        """构建 SQL 和参数"""
        # SELECT
        sql = f"SELECT {', '.join(self._fields)} FROM {self.table}"
        
        # WHERE
        params = []
        if self._conditions:
            where_parts = []
            for cond, cond_params in self._conditions:
                where_parts.append(cond)
                params.extend(cond_params)
            sql += " WHERE " + " AND ".join(where_parts)
        
        # ORDER BY
        if self._order_by:
            sql += f" ORDER BY {self._order_by[0]} {self._order_by[1]}"
        
        # LIMIT
        if self._limit_val:
            sql += f" LIMIT ? OFFSET ?"
            params.extend(self._limit_val)
        
        return sql, tuple(params)
    
    def __repr__(self):
        sql, params = self.build()
        return f"SQL: {sql}\nParams: {params}"


# 使用
query = (
    QueryBuilder("users")
    .select("id", "name", "email")
    .where("age > ?", 18)
    .where("city = ?", "北京")
    .order_by("name", "ASC")
    .limit(10, 0)
)

print(query)

总结

今天我们深入学习了 Python 函数参数的高级特性:

  1. 默认参数:为参数提供默认值,但避免使用可变对象作为默认值
  2. *args:收集任意数量的位置参数为元组
  3. **kwargs:收集任意数量的关键字参数为字典
  4. 参数组合:多种参数类型可以组合使用,有固定顺序
  5. 解包与拆包:使用 *** 在调用时展开参数
  6. 仅关键字参数:使用 * 分隔符强制某些参数必须用关键字传递
  7. 类型注解:使用 : type-> type 提供类型提示

掌握这些知识可以编写出更加灵活和强大的 Python 函数。