Python *args 和 **kwargs 详解:完整指南
Updated on
每个 Python 开发者都会在某个时刻遇到这道坎。当你打开某个库的源代码、同事的拉取请求或开源项目时,你会看到函数签名中到处充斥着 *args 和 **kwargs。这些星号是什么意思?为什么有一个和两个之分?应该在什么时候使用哪一个?使用不当会产生令人困惑的 TypeError 消息,比如 "takes 2 positional arguments but 5 were given"(接受 2 个位置参数但给了 5 个)或 "got an unexpected keyword argument"(得到了意外的关键字参数),突然之间,一个简单的函数调用变成了一场调试会话。
当你需要自己编写灵活的函数时,问题会变得更糟。硬编码每个参数会让你的 API 变得僵化。接受太多命名参数会让函数签名难以阅读。你需要一种方法来编写能够接受可变数量参数的函数,同时又不牺牲清晰度。
Python 通过两个特殊语法特性解决了这个问题:*args 用于可变位置参数,**kwargs 用于可变关键字参数。本指南将从基础开始解释这两者,涵盖解包操作符,介绍实际应用模式,并帮助你避免最常见的错误。
什么是 *args 和 **kwargs?
在 Python 中,*args 和 **kwargs 是用于在函数定义中接受可变数量参数的约定。
*args将额外的位置参数收集到一个**元组(tuple)**中。**kwargs将额外的关键字参数收集到一个**字典(dictionary)**中。
args 和 kwargs 这些名称只是约定,不是强制要求。真正起作用的是 * 和 ** 前缀,而不是名称本身。你可以写成 *values 和 **options,它们的工作方式完全相同。
以下是最简单的演示:
def show_args(*args, **kwargs):
print(f"args = {args}")
print(f"kwargs = {kwargs}")
show_args(1, 2, 3, name="Alice", age=30)
# args = (1, 2, 3)
# kwargs = {'name': 'Alice', 'age': 30}位置参数(不带名称传递的值)作为元组进入 args。关键字参数(使用 key=value 语法传递的值)作为字典进入 kwargs。这就是整个核心概念。
理解 *args:可变位置参数
参数名前的单个星号 * 告诉 Python 将所有剩余的位置参数打包成一个元组。这让你的函数可以接受任意数量的位置值。
基本语法和用法
def add_all(*args):
"""Sum any number of values."""
total = 0
for num in args:
total += num
return total
print(add_all(1, 2)) # 3
print(add_all(1, 2, 3, 4, 5)) # 15
print(add_all(10)) # 10
print(add_all()) # 0在函数内部,args 是一个普通的 Python 元组。你可以遍历它、索引它、检查它的长度,并将其传递给其他函数。
def describe_args(*args):
print(f"Type: {type(args)}")
print(f"Length: {len(args)}")
print(f"First element: {args[0] if args else 'N/A'}")
print(f"Contents: {args}")
describe_args("hello", 42, True)
# Type: <class 'tuple'>
# Length: 3
# First element: hello
# Contents: ('hello', 42, True)将常规参数与 *args 结合使用
你可以将标准参数与 *args 混合使用。所有常规位置参数会先被填充,任何剩余的位置参数都会进入 *args:
def log_message(level, *args):
"""Log a message with a severity level."""
message = " ".join(str(a) for a in args)
print(f"[{level.upper()}] {message}")
log_message("info", "Server started on port", 8080)
# [INFO] Server started on port 8080
log_message("error", "Connection failed:", "timeout after", 30, "seconds")
# [ERROR] Connection failed: timeout after 30 seconds实际示例:灵活的平均值函数
def average(*values):
"""Calculate the average of any number of values."""
if not values:
raise ValueError("average() requires at least one argument")
return sum(values) / len(values)
print(average(85, 90, 78)) # 84.33333333333333
print(average(100)) # 100.0
print(average(72, 88, 95, 67, 91)) # 82.6实际示例:字符串格式化辅助函数
def build_path(*segments):
"""Join path segments with forward slashes, stripping extras."""
cleaned = [seg.strip("/") for seg in segments if seg]
return "/" + "/".join(cleaned)
print(build_path("api", "v2", "users", "123"))
# /api/v2/users/123
print(build_path("/data/", "/reports/", "2026/", "sales.csv"))
# /data/reports/2026/sales.csv理解 **kwargs:可变关键字参数
参数名前的双星号 ** 告诉 Python 将所有剩余的关键字参数打包成一个字典。这让你的函数可以接受任意数量的命名值。
基本语法和用法
def print_info(**kwargs):
"""Print key-value pairs in a formatted way."""
for key, value in kwargs.items():
print(f" {key}: {value}")
print_info(name="Alice", age=30, city="Seattle")
# name: Alice
# age: 30
# city: Seattle在函数内部,kwargs 是一个标准的 Python 字典。你可以使用 .get()、.keys()、.values()、.items() 以及任何其他字典方法。
def describe_kwargs(**kwargs):
print(f"Type: {type(kwargs)}")
print(f"Keys: {list(kwargs.keys())}")
print(f"Values: {list(kwargs.values())}")
describe_kwargs(x=10, y=20, z=30)
# Type: <class 'dict'>
# Keys: ['x', 'y', 'z']
# Values: [10, 20, 30]实际示例:配置构建器
def create_connection(host, port, **kwargs):
"""Create a database connection with optional settings."""
config = {
"host": host,
"port": port,
"timeout": kwargs.get("timeout", 30),
"retries": kwargs.get("retries", 3),
"ssl": kwargs.get("ssl", True),
"pool_size": kwargs.get("pool_size", 5),
}
# Add any extra settings the caller provided
for key, value in kwargs.items():
if key not in config:
config[key] = value
return config
# Basic usage
basic = create_connection("localhost", 5432)
print(basic)
# {'host': 'localhost', 'port': 5432, 'timeout': 30, 'retries': 3, 'ssl': True, 'pool_size': 5}
# With custom options
custom = create_connection(
"db.example.com", 5432,
timeout=60,
ssl=False,
application_name="my_app"
)
print(custom)
# {'host': 'db.example.com', 'port': 5432, 'timeout': 60, 'retries': 3, 'ssl': False, 'pool_size': 5, 'application_name': 'my_app'}实际示例:HTML 标签构建器
def html_tag(tag, content="", **attributes):
"""Generate an HTML tag with optional attributes."""
attr_str = ""
for key, value in attributes.items():
# Convert Python naming to HTML (class_ -> class)
html_key = key.rstrip("_")
attr_str += f' {html_key}="{value}"'
if content:
return f"<{tag}{attr_str}>{content}</{tag}>"
return f"<{tag}{attr_str} />"
print(html_tag("a", "Click here", href="https://example.com", class_="btn"))
# <a href="https://example.com" class="btn">Click here</a>
print(html_tag("img", src="photo.jpg", alt="A photo", width="200"))
# <img src="photo.jpg" alt="A photo" width="200" />
print(html_tag("p", "Hello world", id="intro", style="color: blue"))
# <p id="intro" style="color: blue">Hello world</p>同时使用 *args 和 **kwargs
你可以在同一个函数定义中使用这两者来接受位置参数和关键字参数的任意组合。参数的顺序遵循严格的规则。
参数排序规则
Python 在函数签名中强制要求以下确切顺序:
- 常规位置参数
*args(可变位置参数)- 仅限关键字参数(在
*args之后) **kwargs(可变关键字参数)
def example(a, b, *args, option=True, **kwargs):
print(f"a = {a}")
print(f"b = {b}")
print(f"args = {args}")
print(f"option = {option}")
print(f"kwargs = {kwargs}")
example(1, 2, 3, 4, 5, option=False, color="red", size=10)
# a = 1
# b = 2
# args = (3, 4, 5)
# option = False
# kwargs = {'color': 'red', 'size': 10}以下是参数类型及其顺序的总结:
| 位置 | 类型 | 语法 | 示例 | 描述 |
|---|---|---|---|---|
| 第1位 | 位置参数 | param | a, b | 必需,按位置填充 |
| 第2位 | 默认参数 | param=value | c=10 | 可选,可按位置或名称填充 |
| 第3位 | 可变位置参数 | *args | *args | 收集额外的位置参数 |
| 第4位 | 仅限关键字参数 | param (在 * 之后) | option=True | 必须按名称传递 |
| 第5位 | 可变关键字参数 | **kwargs | **kwargs | 收集额外的关键字参数 |
常见模式:透传函数
使用 *args 和 **kwargs 最有用的模式之一是创建将所有参数传递给另一个函数的函数:
def timed_call(func, *args, **kwargs):
"""Call a function and measure its execution time."""
import time
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
def expensive_sum(a, b, c):
import time
time.sleep(0.1)
return a + b + c
result = timed_call(expensive_sum, 10, 20, c=30)
# expensive_sum took 0.1003s
print(result)
# 60使用 * 和 ** 进行解包
* 和 ** 操作符可以双向工作:它们在函数定义中打包参数,在函数调用和其他上下文中解包参数。
使用 * 解包列表和元组
使用 * 将可迭代对象解包为单独的位置参数:
def add(a, b, c):
return a + b + c
numbers = [10, 20, 30]
# Without unpacking - this causes TypeError
# add(numbers) # TypeError: add() missing 2 required positional arguments
# With unpacking - spreads list into separate arguments
result = add(*numbers)
print(result) # 60
# Works with tuples, sets, and any iterable
coords = (5, 10, 15)
print(add(*coords)) # 30你也可以在赋值和列表构造中使用 * 进行解包(Python 3.5+):
# Extended unpacking in assignments
first, *middle, last = [1, 2, 3, 4, 5]
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5
# Unpacking in list/tuple construction
list_a = [1, 2, 3]
list_b = [4, 5, 6]
combined = [*list_a, *list_b]
print(combined) # [1, 2, 3, 4, 5, 6]
# Unpacking with additional elements
extended = [0, *list_a, 99, *list_b, 100]
print(extended) # [0, 1, 2, 3, 99, 4, 5, 6, 100]使用 ** 解包字典
使用 ** 将字典解包为关键字参数:
def create_user(name, email, role="viewer"):
return {"name": name, "email": email, "role": role}
user_data = {"name": "Alice", "email": "alice@example.com", "role": "admin"}
# Unpack dict into keyword arguments
user = create_user(**user_data)
print(user)
# {'name': 'Alice', 'email': 'alice@example.com', 'role': 'admin'}使用 ** 合并字典
** 最实用的用途之一是合并字典:
defaults = {"color": "blue", "size": 12, "font": "Arial"}
user_prefs = {"color": "red", "size": 16}
# Merge: user_prefs override defaults
merged = {**defaults, **user_prefs}
print(merged)
# {'color': 'red', 'size': 16, 'font': 'Arial'}
# Python 3.9+ also supports the | operator
merged_new = defaults | user_prefs
print(merged_new)
# {'color': 'red', 'size': 16, 'font': 'Arial'}
# Adding extra keys during merge
final = {**defaults, **user_prefs, "theme": "dark"}
print(final)
# {'color': 'red', 'size': 16, 'font': 'Arial', 'theme': 'dark'}结合使用 * 和 ** 解包
在调用函数时可以同时使用这两个操作符:
def report(title, *items, separator="---", **metadata):
print(f"== {title} ==")
for item in items:
print(f" - {item}")
print(separator)
for key, value in metadata.items():
print(f" {key}: {value}")
positional = ["Task A", "Task B", "Task C"]
options = {"author": "Alice", "date": "2026-02-14"}
report("Sprint Review", *positional, separator="===", **options)
# == Sprint Review ==
# - Task A
# - Task B
# - Task C
# ===
# author: Alice
# date: 2026-02-14实用模式
模式 1:装饰器函数
在生产级 Python 中,*args 和 **kwargs 最常见的用途是编写装饰器。装饰器用一个函数包装另一个函数。由于你事先不知道被包装函数的签名,因此必须使用 *args 和 **kwargs 来转发所有参数:
import functools
import time
def retry(max_attempts=3, delay=1.0):
"""Retry a function up to max_attempts times on failure."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
print(f"Attempt {attempt}/{max_attempts} failed: {e}")
if attempt < max_attempts:
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def fetch_data(url, timeout=10):
"""Simulate fetching data that might fail."""
import random
if random.random() < 0.6:
raise ConnectionError(f"Failed to connect to {url}")
return f"Data from {url}"
# The decorator forwards url and timeout through *args/**kwargs
result = fetch_data("https://api.example.com", timeout=5)
print(result)模式 2:子类 init 转发
在子类化时,你通常需要将构造函数参数转发给父类。*args 和 **kwargs 让这个过程变得简洁:
class Animal:
def __init__(self, name, species, sound="..."):
self.name = name
self.species = species
self.sound = sound
def speak(self):
return f"{self.name} says {self.sound}"
class Dog(Animal):
def __init__(self, *args, breed="Unknown", **kwargs):
super().__init__(*args, **kwargs)
self.breed = breed
def info(self):
return f"{self.name} ({self.breed}) - {self.species}"
# All Animal parameters pass through seamlessly
dog = Dog("Rex", "Canine", sound="Woof!", breed="German Shepherd")
print(dog.speak()) # Rex says Woof!
print(dog.info()) # Rex (German Shepherd) - Canine这种模式在处理复杂的类层次结构时至关重要,尤其是在 Django、Flask 或 SQLAlchemy 等框架中,你需要扩展基类。
模式 3:包装器和代理函数
当你需要拦截或修改函数调用而不改变原始函数时:
def log_call(func):
"""Log every call to a function with its arguments."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"Calling {func.__name__}({signature})")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result!r}")
return result
return wrapper
@log_call
def calculate_discount(price, discount_pct, tax_rate=0.08):
discounted = price * (1 - discount_pct / 100)
return round(discounted * (1 + tax_rate), 2)
calculate_discount(100, 20, tax_rate=0.1)
# Calling calculate_discount(100, 20, tax_rate=0.1)
# calculate_discount returned 88.0模式 4:API 客户端构建器
构建灵活的 API 包装器是一个经典用例:
import json
class APIClient:
def __init__(self, base_url, **default_headers):
self.base_url = base_url.rstrip("/")
self.default_headers = {
"Content-Type": "application/json",
"Accept": "application/json",
**default_headers,
}
def request(self, method, endpoint, *args, **kwargs):
"""Build a request with merged headers and parameters."""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
headers = {**self.default_headers, **kwargs.pop("headers", {})}
request_info = {
"method": method,
"url": url,
"headers": headers,
**kwargs,
}
print(json.dumps(request_info, indent=2, default=str))
return request_info
def get(self, endpoint, **kwargs):
return self.request("GET", endpoint, **kwargs)
def post(self, endpoint, **kwargs):
return self.request("POST", endpoint, **kwargs)
# Usage
client = APIClient(
"https://api.example.com",
Authorization="Bearer token123"
)
client.get("/users", params={"page": 1, "limit": 50})
client.post("/users", data={"name": "Alice"}, headers={"X-Request-ID": "abc123"})模式 5:数据科学——动态绘图参数
在构建数据分析函数时,**kwargs 允许你将配置传递给底层库:
import pandas as pd
def analyze_column(df, column, **plot_kwargs):
"""Analyze a dataframe column and generate summary statistics."""
stats = {
"count": df[column].count(),
"mean": df[column].mean(),
"std": df[column].std(),
"min": df[column].min(),
"max": df[column].max(),
}
print(f"\nAnalysis of '{column}':")
for stat, value in stats.items():
print(f" {stat}: {value:.2f}")
# Forward any extra kwargs to the plot function
plot_defaults = {"kind": "hist", "bins": 20, "title": f"Distribution of {column}"}
plot_config = {**plot_defaults, **plot_kwargs}
# df[column].plot(**plot_config) # Uncomment with matplotlib installed
print(f" Plot config: {plot_config}")
return stats
# Create sample data
df = pd.DataFrame({
"revenue": [100, 250, 180, 320, 275, 410, 195, 360],
"quantity": [5, 12, 8, 15, 13, 20, 9, 17],
})
# Default analysis
analyze_column(df, "revenue")
# Custom plot settings passed through **kwargs
analyze_column(df, "revenue", kind="box", color="steelblue", figsize=(10, 6))如果你在 Jupyter 笔记本中工作并希望以交互方式试验函数签名,RunCell (opens in a new tab) 提供了一个由 AI 驱动的笔记本环境,你可以在其中测试 *args 和 **kwargs 模式,获得参数处理的实时建议,并调试参数传递问题,而无需离开你的工作流程。
常见错误及修复方法
以下是 Python 开发者在使用 *args 和 **kwargs 时遇到的最频繁错误及其解决方案:
| 错误 | 错误信息 | 原因 | 修复方法 |
|---|---|---|---|
| 参数顺序错误 | SyntaxError: invalid syntax | 将 **kwargs 放在 *args 之前 | 始终使用顺序:常规参数、*args、仅限关键字参数、**kwargs |
| 传递列表而不是解包 | TypeError: func() missing required arguments | 传递 [1,2,3] 而不是 *[1,2,3] | 使用 * 解包:func(*my_list) |
| 重复的关键字参数 | TypeError: got multiple values for argument 'x' | 同一个键同时出现在位置参数和 **kwargs 中 | 确保位置参数和字典键之间没有重叠 |
| 直接修改 kwargs | 意外的副作用 | 修改 kwargs 字典 | 使用 kwargs.copy() 或 {**kwargs, ...} |
| 在 super().init 中忘记 *args | TypeError: __init__() missing arguments | 没有将参数转发给父类 | 使用 super().__init__(*args, **kwargs) |
| 使用可变默认参数和 kwargs | 调用之间共享状态 | def func(data={}) | 使用 None 作为默认值:def func(data=None) |
示例:重复的关键字参数
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
data = {"name": "Alice", "greeting": "Hi"}
# WRONG: name 同时作为位置参数和通过 **data 传递
# greet("Alice", **data)
# TypeError: greet() got multiple values for argument 'name'
# CORRECT: 只通过解包传递
print(greet(**data))
# Hi, Alice!
# OR: 移除重复的键
print(greet("Alice", **{"greeting": "Hi"}))
# Hi, Alice!示例:安全地修改 kwargs
def process(name, **kwargs):
# WRONG: 直接修改 kwargs 会影响调用者的字典
# kwargs["processed"] = True
# CORRECT: 创建一个新字典
config = {**kwargs, "processed": True}
return {"name": name, **config}
settings = {"timeout": 30, "retries": 3}
result = process("task1", **settings)
print(result)
# {'name': 'task1', 'timeout': 30, 'retries': 3, 'processed': True}
# Original dict is unchanged
print(settings)
# {'timeout': 30, 'retries': 3}*args/**kwargs 与其他方法的对比
什么时候应该使用 *args 和 **kwargs 而不是其他替代方案?以下是对比:
| 方法 | 语法 | 最适合 | 缺点 |
|---|---|---|---|
*args | def f(*args) | 未知数量的同类型位置值 | 没有每个参数的类型提示,无法通过名称访问 |
**kwargs | def f(**kwargs) | 灵活的选项,传递给其他函数 | IDE 中没有自动补全,没有静态类型检查 |
| 显式参数 | def f(a, b, c) | 已知、固定的参数集 | 僵化;添加参数会破坏现有调用 |
| 默认参数 | def f(a, b=10) | 具有合理默认值的可选参数 | 仍然需要预先知道所有选项 |
| 列表/元组参数 | def f(items: list) | 有序的值集合 | 调用者必须显式构造列表 |
| 字典参数 | def f(options: dict) | 结构化的配置 | 调用者必须显式构造字典 |
| TypedDict/dataclass | def f(config: Config) | 结构化、类型安全的配置 | 更多样板代码,需要类定义 |
一般准则:
- 当参数集已知且稳定时,使用显式参数。
- 当你的函数自然地对可变数量的同类型值进行操作时(如
print()、max()、min()),使用*args。 - 当你需要将选项转发给另一个函数、接受灵活的配置或构建可扩展的 API 时,使用
**kwargs。 - 编写装饰器、代理函数或需要完全参数转发的类层次结构时,同时使用两者。
- 当你希望 IDE 自动补全和静态类型检查结构化配置时,使用 TypedDict 或 dataclass。
为 *args 和 **kwargs 添加类型提示
Python 3.11+ 允许你使用 Unpack 和 TypedDict 为 *args 和 **kwargs 添加类型提示:
# Basic type hints (all args same type)
def add_numbers(*args: float) -> float:
return sum(args)
def set_options(**kwargs: str) -> dict[str, str]:
return kwargs
# Python 3.11+: precise kwargs typing with TypedDict
from typing import TypedDict, Unpack
class ConnectionOptions(TypedDict, total=False):
timeout: int
retries: int
ssl: bool
def connect(host: str, port: int, **kwargs: Unpack[ConnectionOptions]) -> dict:
return {"host": host, "port": port, **kwargs}
# IDE now knows the valid keyword arguments
result = connect("localhost", 5432, timeout=30, ssl=True)
print(result)
# {'host': 'localhost', 'port': 5432, 'timeout': 30, 'ssl': True}常见问题解答
*args 和 **kwargs 代表什么?
args 和 kwargs 这些名称分别是 "arguments"(参数)和 "keyword arguments"(关键字参数)的缩写。它们纯粹是约定俗成的名称。单星号 * 告诉 Python 将额外的位置参数打包成元组,而双星号 ** 将额外的关键字参数打包成字典。你会在几乎每一个 Python 代码库、教程和库中看到这些名称,因为它们是普遍认可的约定,但语言本身并不要求这些特定的名称。
我可以使用 args 和 kwargs 以外的名称吗?
可以。args 和 kwargs 是约定,不是语法要求。解包行为完全来自于 * 和 ** 前缀。你可以写成 *values、*items、*numbers、**options、**config、**params 或任何其他有效的 Python 标识符。然而,在大多数情况下强烈建议坚持使用 *args 和 **kwargs,因为每个 Python 开发者都能立即识别它们。只有在更具描述性的名称真正能提高可读性时才使用自定义名称,例如在文件处理函数中使用 *paths 或在 HTTP 客户端中使用 **headers。
Python 函数中参数的正确顺序是什么?
Python 强制要求严格的顺序:常规位置参数在前,然后是 *args,接着是仅限关键字参数(带默认值),最后是 **kwargs。完整顺序是:def func(pos1, pos2, default1=val, *args, kw_only1, kw_only2=val, **kwargs)。违反此顺序会产生 SyntaxError。一个有用的记忆法是 "positional, star-args, keyword-only, double-star-kwargs"——星号的数量从左到右递增。
什么时候应该使用 *args 而不是列表参数?
当每个参数都是独立的、不相关的值,且调用者应该自然地传递它们而无需构造容器时,使用 *args:print("a", "b", "c") 比 print(["a", "b", "c"]) 更自然。当值在逻辑上形成一个调用者已经在变量中拥有的集合,或者当你需要区分集合和其他参数时,使用列表参数。像 max()、min() 和 print() 这样的内置函数使用 *args,因为调用约定感觉自然,而像 sorted(iterable) 这样的函数接受单个可迭代对象,因为输入本质上是一个序列。
*args 和 **kwargs 慢吗?
*args 和 **kwargs 的开销极小。Python 在每次调用时为 *args 创建元组,为 **kwargs 创建字典,这涉及少量内存分配。在基准测试中,与显式参数相比的差异通常是每次调用几百纳秒——对于几乎所有真实世界的代码来说都无关紧要。你需要在紧密循环中进行数百万次调用,这种开销才会变得可测量。专注于算法效率和 I/O 优化,而不是避免使用 *args/**kwargs。它们提供的灵活性和代码可维护性远远超过了任何微小的性能成本。
结论
Python 的 *args 和 **kwargs 是这门语言中最实用的两个特性。它们解决了一个基本问题:如何编写足够灵活的函数来接受可变数量的参数,同时又不牺牲可读性。
关键要点:
*args将额外的位置参数收集到元组中。当你的函数应该接受任意数量的值时使用它。**kwargs将额外的关键字参数收集到字典中。当你需要将选项转发给另一个函数、接受灵活的配置或构建可扩展的 API 时使用它。- 参数顺序始终是:常规参数、
*args、仅限关键字参数、**kwargs。 - 解包使用
*和**可以在函数调用、列表构造和字典合并中使用。 - 装饰器是最重要的实际应用案例——它们依赖
*args和**kwargs来包装任何函数,无论其签名如何。
对于具有已知、稳定签名的函数,从显式参数开始。当你需要灵活性时,再使用 *args 和 **kwargs:装饰器、子类转发、包装器函数和 API 构建器。一旦你内化了打包和解包的机制,你会发现自己编写的 Python 代码更简洁、更可重用。