Python——解包与*args和**kwargs参数


整体说明

  • 在 Python 中,***的主要用途有两个
    • 作为解包操作符 :用于解包(unpacking)序列或字典
    • 在函数定义和调用时处理可变参数
      • * 用于接收可变数量的位置参数,被称为 “可变位置参数”(variable positional arguments)
      • ** 用于接收可变数量的关键字参数,被称为 “可变关键字参数”(variable keyword arguments)

*解包操作(序列解包)

  • *操作符用于解包可迭代对象(如列表、元组、字符串、生成器等),将其元素单独提取出来

函数调用时解包

  • 函数调用时解包参数 Demo:
    1
    2
    3
    4
    5
    def add(a, b, c):
    return a + b + c
    numbers = [1, 2, 3]
    result = add(*numbers) # 等同于 add(1, 2, 3)
    print(result) # 输出: 6

在赋值语句中解包

  • 赋值语句中解包 Demo
    1
    2
    3
    4
    a, *b, c = [1, 2, 3, 4, 5]
    print(a) # 输出: 1
    print(b) # 输出: [2, 3, 4]
    print(c) # 输出: 5

合并列表或元组

  • 合并列表或元组时解包 Demo
    1
    2
    3
    4
    5
    6
    7
    8
    # 合并列表
    list1 = [1, 2]
    list2 = [3, 4]
    merged = [*list1, *list2] # 等同于 [1, 2, 3, 4]
    # 合并元组和列表
    t = (1, 2)
    l = [3, 4]
    merged = [*t, *l] # 等同于 [1, 2, 3, 4]

**解包操作(字典解包)

  • **操作符用于解包字典 ,将其键值对作为参数传递给函数

在函数调用时解包字典

  • 函数调用时解包 Demo:
    1
    2
    3
    4
    5
    def greet(name, age):
    return f"Hello {name}, you are {age} years old."
    person = {"name": "Alice", "age": 30}
    message = greet(**person) # 等同于 greet(name="Alice", age=30)
    print(message) # 输出: "Hello Alice, you are 30 years old."

合并字典(Python 3.5 以上版本才能使用)

  • 合并字典解包 Demo:
    1
    2
    3
    dict1 = {"a": 1, "b": 2}
    dict2 = {"c": 3, "d": 4}
    merged = {**dict1, **dict2} # 等同于 {"a": 1, "b": 2, "c": 3, "d": 4}

*args作为函数参数(可变位置参数)

  • *args用于接收任意数量位置参数(也称为非关键字参数),并将它们作为元组处理

  • *args作为函数参数 Demo

    1
    2
    3
    4
    5
    6
    7
    def sum_numbers(*args):
    total = 0
    for num in args:
    total += num
    return total
    result = sum_numbers(1, 2, 3, 4) # args = (1, 2, 3, 4)
    print(result) # 输出: 10
  • 说明:

    • args元组类型(解包时可以解所有序列,包括列表、元组、字符串、生成器等)
    • 可以不传递参数,此时args为空元组
    • 定义时*args必须放在普通参数和**kwargs之间(如def func(a, *args, **kwargs)

**kwargs作为函数参数(可变关键字参数)

  • **kwargs用于接收任意数量的关键字参数 ,并将它们作为字典处理

  • **kwargs作为函数参数 Demo:

    1
    2
    3
    4
    5
    6
    7
    8
    def print_info(**kwargs):
    for key, value in kwargs.items():
    print(f"{key}: {value}")
    print_info(name="Bob", age=25, city="New York")
    # 输出:
    # name: Bob
    # age: 25
    # city: New York
  • 说明:

    • kwargs字典类型
    • 可以不传递参数,此时kwargs为空字典
    • 必须放在参数列表的最后(如def func(a, *args, b=1, **kwargs)

组合使用*args**kwargs

  • 函数可以同时接受*args**kwargs,顺序必须是:def func(positional, *args, **kwargs)

  • 组合使用*args**kwargs的 Demo:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def example(a, *args, **kwargs):
    print(f"Positional: {a}") # 必传参数
    print(f"Args: {args}") # 可选的位置参数
    print(f"Kwargs: {kwargs}") # 可选的关键字参数
    example(1, 2, 3, x=4, y=5)
    # 输出:
    # Positional: 1
    # Args: (2, 3)
    # Kwargs: {'x': 4, 'y': 5}
  • 说明:

    • 普通参数也可以不传递,此时只剩下*args**kwargs两种参数

    • 在使用了**kwargs时,也可以继续使用关键字参数,但必须放到*args**kwargs之间

      1
      2
      3
      4
      5
      6
      7
      def example(a, *args, y=10, **kwargs) 
      # def example(a, *args, **kwargs, y=10) # error
      ```
      * 注:函数调用时,关键字参数和`**kwargs`的顺序没有限制,但位置参数的位置不能变化
      ```python
      def example(a, *args, y=10, **kwargs)
      def example(1, 2, 3, x=4, y=5, y=10) # 关键字参数 y 的顺序变化是可以的
    • 深入理解:放到*args**kwargs之间的参数,必须按照关键字参数使用(定义时可以位置式的样子定义)

      1
      2
      3
      def example(a, *args, n, **kwargs)
      example(1, 2, 3, n=10, x=4, y=5) # 正确,n 应该指定关键字参数使用
      # example(1, 2, 3, 10, x=4, y=5) # 报错,n 应该指定关键字参数使用

常见用法

函数包装器(Decorator)透传参数

  • 函数包装器透传参数 Demo:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def my_decorator(func):
    def wrapper(*args, **kwargs):
    print("Before function call")
    result = func(*args, **kwargs) # 传递所有参数给原函数
    print("After function call")
    return result
    return wrapper

    @my_decorator
    def add(a, b):
    return a + b

继承中的参数透传

  • 继承时参数透传 Demo:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Parent:
    def __init__(self, name, **kwargs):
    self.name = name

    class Child(Parent):
    def __init__(self, age, *args, **kwargs):
    super().__init__(*args, **kwargs) # 传递剩余参数给父类
    self.age = age

    c = Child(age=10, name="Alice")

使用注意事项

  • *args非关键字参数(也称为位置参数),对应参数args元组类型,可表示任何多个无名参数

  • **kwargs关键字参数 ,对应参数kwargs字典(dict)类型,可表示任何多个命名参数

  • 顺序必须是:1)普通参数;2)*args;3)**kwargs的形式

    • 举例:def func(a, *args, **kwargs)
    • 其中普通参数、*args**kwargs三个参数都是可选的,可以丢弃任意一个,但是相对顺序必须保障
    • 放在 *args**kwargs之间的参数一定是关键字参数
  • 将序列解包为函数参数时要保证参数数量匹配

    1
    2
    3
    4
    def func(a, b):
    return a + b
    args = [1, 2, 3]
    func(*args) # 报错:TypeError,参数数量不匹配
  • 将字典解包为函数参数时要保证键名匹配

    1
    2
    3
    4
    def func(name, age):
    pass
    kwargs = {"name": "Alice", "city": "NY"} # 键名不匹配
    func(**kwargs) # 报错:TypeError,'city' 不是有效参数

附录:特殊用法指定参数为关键字参数

  • 如果在函数定义时使用 *,则后面的所有参数必须通过关键字形式传入
  • 举例:
    1
    2
    3
    # 调用 f 函数时,b 和 c 必须使用关键字传入,否则出错
    def f(a, *, b=1, c=None)
    pass