Python中*和**的5个实战用法:从函数传参到字典合并
Python星号操作符的深度实战:从参数魔法到数据解构的五个核心场景
如果你在Python社区混迹过一段时间,肯定不止一次见过*args和**kwargs这样的写法。这两个看似简单的星号操作符,实际上是Python语言设计中最具表现力的语法糖之一。很多开发者只是机械地使用它们来处理不定参数,却很少深入思考它们背后的设计哲学和更广泛的应用场景。实际上,单星号*和双星号**在Python中扮演着远比表面看起来更丰富的角色——从函数参数传递的灵活性,到数据结构的优雅解包,再到现代Python代码中的模式匹配,它们无处不在。
我记得刚开始接触Python时,看到别人代码中的*args总觉得很神秘,后来才发现这不过是Python“鸭子类型”哲学的一个具体体现:不关心你传给我什么,只关心我能否处理。而**kwargs则进一步扩展了这种灵活性,让函数接口可以像字典一样动态扩展。但真正让我意识到这两个操作符威力的,是在处理API响应、配置合并和数据处理流水线时——那些看似复杂的操作,用星号操作符往往能写出既简洁又富有表达力的代码。
本文将带你超越基础的“不定参数”用法,深入探索*和**在五个实际开发场景中的高级应用。无论你是要设计更灵活的API接口,还是处理复杂的数据转换任务,这些技巧都能让你的代码更加Pythonic。
1. 函数参数系统的完全掌控
1.1 理解参数传递的四个层次
Python的函数参数系统可能是所有主流语言中最灵活的之一,而星号操作符正是这种灵活性的核心。要真正掌握它们,我们需要从参数传递的四个层次来理解:
- 位置参数 - 最基本的参数传递方式
- 关键字参数 - 通过参数名指定值
- 可变位置参数(
*args)- 接收任意数量的位置参数 - 可变关键字参数(
**kwargs)- 接收任意数量的关键字参数
def process_data(name, age=25, *scores, **metadata):
"""展示四种参数类型的典型用法"""
print(f"姓名: {name}")
print(f"年龄: {age}")
print(f"成绩列表: {scores}")
print(f"元数据: {metadata}")
# 调用示例
process_data("张三", 30, 85, 92, 78, city="北京", department="研发")
这个简单的例子展示了标准参数顺序:必选参数 → 默认参数 → *args → **kwargs。但实际开发中,我们经常需要更精细的控制。
1.2 强制关键字参数:Python 3的隐藏特性
Python 3引入了一个不太为人知但极其有用的特性:在函数定义中使用单独的*可以强制后面的参数必须以关键字形式传递。这在设计API时特别有用,可以避免参数顺序错误导致的bug。
def create_user(username, *, email, phone, is_active=True):
"""
创建用户账户
* 后面的所有参数必须使用关键字形式传递
"""
user_data = {
'username': username,
'email': email,
'phone': phone,
'is_active': is_active
}
return user_data
# 正确调用
user1 = create_user("john_doe", email="john@example.com", phone="13800138000")
# 错误调用 - 会抛出TypeError
# user2 = create_user("jane_doe", "jane@example.com", "13900139000")
提示:在设计需要向后兼容的API时,强制关键字参数可以确保新增参数不会破坏现有调用。当你在函数签名中添加新参数时,将其放在
*后面,这样现有代码不需要修改就能继续工作。
1.3 参数转发模式:装饰器和中间件的核心
星号操作符在装饰器和中间件模式中大放异彩。通过*args和**kwargs,我们可以创建能够包装任何函数的通用装饰器。
def log_execution_time(func):
"""记录函数执行时间的装饰器"""
import time
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs) # 关键:原样转发所有参数
end_time = time.time()
print(f"{func.__name__} 执行时间: {end_time - start_time:.4f}秒")
return result
return wrapper
@log_execution_time
def complex_calculation(n, method='fast', precision=0.001):
"""模拟复杂计算"""
import time
time.sleep(0.5) # 模拟计算耗时
return n * 2
# 装饰器自动适应被装饰函数的参数
result1 = complex_calculation(10)
result2 = complex_calculation(20, method='accurate', precision=0.0001)
这种参数转发模式在Web框架的中间件、数据库连接池、缓存装饰器等场景中无处不在。它的强大之处在于,装饰器完全不需要知道被装饰函数的具体参数签名。
2. 序列解包的艺术与科学
2.1 基础解包:超越简单的变量赋值
大多数Python开发者都知道可以用a, b = (1, 2)这样的方式解包元组,但星号操作符让解包能力提升到了新的层次。特别是在处理不确定长度的序列时,*操作符提供了优雅的解决方案。
# 基础解包
first, *middle, last = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(f"第一个: {first}") # 1
print(f"中间部分: {middle}") # [2, 3, 4, 5, 6, 7, 8]
print(f"最后一个: {last}") # 9
# 处理CSV数据行的典型场景
def parse_csv_row(row):
"""解析CSV行,第一列是ID,最后一列是时间戳,中间是数据字段"""
row_id, *data_fields, timestamp = row.split(',')
return {
'id': int(row_id),
'data': [float(x) for x in data_fields],
'timestamp': timestamp.strip()
}
# 示例数据
csv_row = "1001,23.5,18.7,31.2,19.8,2023-10-01 14:30:00"
parsed = parse_csv_row(csv_row)
print(parsed)
2.2 嵌套解包:处理复杂数据结构
当数据结构变得复杂时,嵌套解包展示了Python语法的强大表现力。结合Python 3.10引入的模式匹配,这种能力更加强大。
# 复杂数据结构的解包
data = [
("张三", 30, ["Python", "Java", "Go"]),
("李四", 25, ["JavaScript", "TypeScript"]),
("王五", 35, ["C++", "Rust", "Python", "Java"])
]
for name, age, *languages in data:
primary_lang, *other_langs = languages if languages else ["未知"]
print(f"{name}({age}岁) 主要语言: {primary_lang}, 其他语言: {other_langs}")
# 处理API响应中的嵌套数据
api_response = {
"status": "success",
"data": {
"users": [
{"id": 1, "name": "Alice", "roles": ["admin", "editor"]},
{"id": 2, "name": "Bob", "roles": ["viewer"]}
],
"metadata": {"page": 1, "total": 2}
}
}
# 使用解包提取嵌套数据
status, data = api_response["status"], api_response["data"]
users, metadata = data["users"], data["metadata"]
# 进一步解包用户数据
for user in users:
user_id, username, *roles = user["id"], user["name"], user["roles"]
print(f"用户{user_id}: {username}, 角色: {roles}")
2.3 星号表达式在迭代中的应用
在迭代过程中使用星号解包,可以写出非常简洁的代码来处理复杂的数据转换。
# 矩阵转置的优雅实现
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# 传统方式
transposed = []
for i in range(len(matrix[0])):
transposed.append([row[i] for row in matrix])
# 使用zip和*的Pythonic方式
transposed_pythonic = list(zip(*matrix))
print(f"转置矩阵: {transposed_pythonic}")
# 更实用的例子:合并多个列表的对应元素
sales_q1 = [100, 150, 200]
sales_q2 = [120, 160, 210]
sales_q3 = [130, 170, 220]
sales_q4 = [140, 180, 230]
# 计算每个产品的年度销售总额
annual_sales = [sum(quarter) for quarter in zip(sales_q1, sales_q2, sales_q3, sales_q4)]
print(f"年度销售额: {annual_sales}")
# 或者更简洁地
all_sales = [sales_q1, sales_q2, sales_q3, sales_q4]
annual_sales_alt = [sum(product_sales) for product_sales in zip(*all_sales)]
注意:虽然
zip(*matrix)这种写法很优雅,但在处理非常大的矩阵时需要注意内存使用。zip函数返回的是迭代器,但*操作符会解包所有参数,如果矩阵非常大,可能会消耗较多内存。
3. 字典操作的高级技巧
3.1 字典合并的三种范式
在Python 3.5之前,合并字典是个有点繁琐的操作。现在,**操作符让字典合并变得直观而强大。但不同的合并方式有不同的适用场景。
# 三种字典合并方式对比
base_config = {"host": "localhost", "port": 8080, "timeout": 30}
user_config = {"port": 9000, "debug": True}
environment_config = {"host": "prod-server", "max_connections": 100}
# 方法1: update() - 原地修改
config1 = base_config.copy() # 先复制,避免修改原字典
config1.update(user_config)
config1.update(environment_config)
print(f"update方法: {config1}")
# 方法2: {**a, **b} - Python 3.5+ 语法
config2 = {**base_config, **user_config, **environment_config}
print(f"**操作符: {config2}")
# 方法3: | 运算符 - Python 3.9+ 语法
config3 = base_config | user_config | environment_config
print(f"| 运算符: {config3}")
这三种方法在大多数情况下结果相同,但有一些细微差别:
| 特性 | update()方法 | **操作符 | | 运算符 | |------|-------------|----------|----------| | Python版本 | 所有版本 | 3.5+ | 3.9+ | | 是否创建新字典 | 否(除非先copy) | 是 | 是 | | 可读性 | 中等 | 高 | 高 | | 链式操作 | 需要多次调用 | 支持 | 支持 | | 性能 | 快 | 中等 | 快 |
3.2 配置系统的深度合并
在实际项目中,配置合并往往不是简单的键值覆盖,而是需要深度合并(deep merge)。**操作符结合递归可以优雅地解决这个问题。
def deep_merge(base_dict, override_dict):
"""深度合并两个字典,递归处理嵌套字典"""
result = base_dict.copy()
for key, value in override_dict.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
# 如果两个值都是字典,递归合并
result[key] = deep_merge(result[key], value)
else:
# 否则直接覆盖(或新增)
result[key] = value
return result
# 复杂配置合并示例
default_config = {
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"username": "admin",
"password": "secret"
}
},
"logging": {
"level": "INFO",
"handlers": ["console"]
}
}
environment_config = {
"database": {
"host": "prod-db.example.com",
"credentials": {
"password": "new_secret_123"
}
},
"logging": {
"level": "WARNING"
},
"cache": {
"enabled": True,
"ttl": 3600
}
}
# 深度合并配置
merged_config = deep_merge(default_config, environment_config)
# 使用**操作符创建最终配置(添加运行时覆盖)
runtime_overrides = {"logging": {"level": "DEBUG"}}
final_config = deep_merge(merged_config, runtime_overrides)
print("深度合并后的配置:")
for section, settings in final_config.items():
print(f" {section}: {settings}")
3.3 字典解包在函数调用中的妙用
**操作符在函数调用时解包字典,这为动态参数传递提供了极大的灵活性。特别是在构建插件系统或处理回调函数时,这种技巧非常有用。
class EventHandler:
"""事件处理器,支持动态注册和触发事件"""
def __init__(self):
self._handlers = {}
def register(self, event_name, handler):
"""注册事件处理器"""
if event_name not in self._handlers:
self._handlers[event_name] = []
self._handlers[event_name].append(handler)
def trigger(self, event_name, **event_data):
"""触发事件,传递事件数据给所有注册的处理器"""
if event_name not in self._handlers:
return
for handler in self._handlers[event_name]:
# 关键:使用**将字典解包为关键字参数
try:
handler(**event_data)
except TypeError as e:
# 处理器可能不接受所有参数,尝试只传递它需要的参数
import inspect
sig = inspect.signature(handler)
params = list(sig.parameters.keys())
# 只传递处理器声明的参数
filtered_data = {k: v for k, v in event_data.items() if k in params}
handler(**filtered_data)
# 定义不同参数需求的事件处理器
def log_user_login(username, ip_address, timestamp):
print(f"[登录] 用户: {username}, IP: {ip_address}, 时间: {timestamp}")
def update_user_session(username, **session_info):
print(f"[会话] 用户: {username}, 会话信息: {session_info}")
def send_welcome_email(username, email):
print(f"[邮件] 发送欢迎邮件给: {username} <{email}>")
# 创建事件处理器并注册
handler = EventHandler()
handler.register("user_login", log_user_login)
handler.register("user_login", update_user_session)
handler.register("user_login", send_welcome_email)
# 触发事件
login_event = {
"username": "张三",
"ip_address": "192.168.1.100",
"timestamp": "2023-10-01 10:30:00",
"email": "zhangsan@example.com",
"user_agent": "Mozilla/5.0",
"device_id": "device_123"
}
handler.trigger("user_login", **login_event)
这种模式在Web框架、GUI应用、游戏引擎等事件驱动系统中非常常见。它允许事件生产者传递丰富的数据,而事件消费者只接收自己关心的部分。
4. 类型提示与星号操作符的现代结合
4.1 使用*和**的类型注解
Python 3.8+的类型提示系统对*args和**kwargs提供了更好的支持。正确的类型注解可以让IDE提供更准确的代码补全和类型检查。
from typing import Any, Callable, TypedDict, Unpack
from typing_extensions import NotRequired # Python 3.11+内置
# 为**kwargs定义明确的类型
class ConnectionParams(TypedDict):
"""数据库连接参数的类型定义"""
host: str
port: int
database: str
username: str
password: str
timeout: NotRequired[int] # 可选参数
ssl: NotRequired[bool] # 可选参数
def connect_to_database(**kwargs: Unpack[ConnectionParams]) -> str:
"""
连接到数据库,使用类型化的关键字参数
注意:Python 3.12+中,Unpack是内置的
对于更早版本,可以使用typing_extensions
"""
# 提取必需参数
host = kwargs["host"]
port = kwargs["port"]
database = kwargs["database"]
username = kwargs["username"]
password = kwargs["password"]
# 处理可选参数
timeout = kwargs.get("timeout", 30)
ssl = kwargs.get("ssl", False)
# 模拟连接逻辑
connection_string = f"{username}:{password}@{host}:{port}/{database}"
if ssl:
connection_string += "?ssl=true"
print(f"连接字符串: {connection_string}, 超时: {timeout}秒")
return connection_string
# 现在IDE会提供参数提示和类型检查
# connect_to_database(
# host="localhost",
# port=5432,
# database="mydb",
# username="admin",
# password="secret",
# timeout=60,
# ssl=True
# )
# 处理可变位置参数的类型提示
from typing import Iterable
def calculate_statistics(*numbers: float, method: str = "mean") -> float:
"""
计算统计量,接受任意数量的浮点数
Args:
*numbers: 任意数量的数值
method: 计算方法,可选"mean"(均值)或"median"(中位数)
Returns:
计算结果
"""
if not numbers:
return 0.0
if method == "mean":
return sum(numbers) / len(numbers)
elif method == "median":
sorted_numbers = sorted(numbers)
n = len(sorted_numbers)
mid = n // 2
if n % 2 == 0:
return (sorted_numbers[mid - 1] + sorted_numbers[mid]) / 2
else:
return sorted_numbers[mid]
else:
raise ValueError(f"未知的计算方法: {method}")
# 使用示例
stats1 = calculate_statistics(1.0, 2.0, 3.0, 4.0, 5.0)
stats2 = calculate_statistics(1.5, 2.5, 3.5, method="median")
4.2 使用*进行仅限关键字参数的现代语法
Python 3.8引入了一个更清晰的语法来定义仅限关键字参数,这比在参数列表中使用单独的*更加明确。
# 传统方式:使用单独的*标记
def process_order(order_id: int, *, priority: bool = False, notify: bool = True) -> dict:
"""处理订单,priority和notify必须是关键字参数"""
return {
"order_id": order_id,
"priority": priority,
"notify": notify,
"status": "processed"
}
# Python 3.8+ 的现代语法
from typing import Literal
def create_api_endpoint(
path: str,
*,
methods: list[Literal["GET", "POST", "PUT", "DELETE"]] = ["GET"],
auth_required: bool = True,
rate_limit: int = 100,
**extra_options: Unpack[dict[str, Any]]
) -> dict:
"""
创建API端点配置
Args:
path: 端点路径
methods: 支持的HTTP方法(仅限关键字参数)
auth_required: 是否需要认证(仅限关键字参数)
rate_limit: 速率限制(仅限关键字参数)
**extra_options: 额外配置选项
"""
endpoint_config = {
"path": path,
"methods": methods,
"auth_required": auth_required,
"rate_limit": rate_limit,
**extra_options # 合并额外选项
}
# 添加默认值处理
if "timeout" not in endpoint_config:
endpoint_config["timeout"] = 30
return endpoint_config
# 调用示例 - methods等参数必须使用关键字形式
api_config = create_api_endpoint(
"/api/users",
methods=["GET", "POST"],
auth_required=True,
rate_limit=200,
timeout=60,
cache_ttl=300
)
print("API配置:")
for key, value in api_config.items():
print(f" {key}: {value}")
4.3 泛型函数与可变参数的类型安全
结合Python 3.12的泛型语法,我们可以创建类型安全的可变参数函数,这在构建库和框架时特别有用。
from typing import TypeVar, Generic, overload
from collections.abc import Sequence
T = TypeVar('T')
U = TypeVar('U')
class Pipeline(Generic[T, U]):
"""数据处理流水线,支持多个处理阶段"""
def __init__(self, *processors: Callable[[T], U]):
self.processors = processors
def process(self, data: T) -> U:
"""按顺序应用所有处理器"""
result = data
for processor in self.processors:
result = processor(result)
return result
def then(self, *next_processors: Callable[[U], U]) -> 'Pipeline[T, U]':
"""添加更多处理器,返回新的流水线"""
return Pipeline(*self.processors, *next_processors)
# 使用示例:文本处理流水线
def to_lowercase(text: str) -> str:
return text.lower()
def remove_punctuation(text: str) -> str:
import string
return text.translate(str.maketrans('', '', string.punctuation))
def remove_stopwords(text: str) -> str:
stopwords = {"the", "a", "an", "and", "or", "but", "in", "on", "at"}
words = text.split()
filtered_words = [word for word in words if word not in stopwords]
return " ".join(filtered_words)
# 创建处理流水线
text_pipeline = Pipeline(to_lowercase, remove_punctuation)
text_pipeline = text_pipeline.then(remove_stopwords)
# 处理文本
sample_text = "The quick brown fox jumps over the lazy dog!"
processed = text_pipeline.process(sample_text)
print(f"原始文本: {sample_text}")
print(f"处理后: {processed}")
# 另一个例子:数值处理流水线
def add_five(x: float) -> float:
return x + 5
def multiply_by_two(x: float) -> float:
return x * 2
def square(x: float) -> float:
return x ** 2
math_pipeline = Pipeline(add_five, multiply_by_two, square)
result = math_pipeline.process(3)
print(f"数学流水线结果: {result}") # ((3 + 5) * 2) ** 2 = 256
5. 实战应用:构建灵活的数据处理框架
5.1 动态查询构建器
在实际的数据处理项目中,我们经常需要构建动态的查询条件。*和**操作符在这方面表现出色,可以让代码既灵活又类型安全。
from typing import Any, Optional
from datetime import datetime
class QueryFilter:
"""灵活的查询过滤器"""
def __init__(self, **conditions):
self.conditions = conditions
self._operations = []
def equals(self, field: str, value: Any) -> 'QueryFilter':
"""等于条件"""
self.conditions[field] = value
return self
def in_range(self, field: str, min_value: Any, max_value: Any) -> 'QueryFilter':
"""范围条件"""
self._operations.append((field, 'range', (min_value, max_value)))
return self
def build(self) -> dict:
"""构建查询字典"""
query = self.conditions.copy()
for field, op, value in self._operations:
if op == 'range':
query[f"{field}__gte"] = value[0]
query[f"{field}__lte"] = value[1]
return query
@classmethod
def combine(cls, *filters: 'QueryFilter', operator: str = 'AND') -> dict:
"""组合多个过滤器"""
if not filters:
return {}
if len(filters) == 1:
return filters[0].build()
combined = {}
for i, filter_obj in enumerate(filters):
prefix = f"filter_{i}_"
for key, value in filter_obj.build().items():
combined[f"{prefix}{key}"] = value
combined["_operator"] = operator
return combined
# 使用示例:构建复杂查询
def query_users(
*,
min_age: Optional[int] = None,
max_age: Optional[int] = None,
country: Optional[str] = None,
registration_date: Optional[datetime] = None,
**extra_filters
) -> dict:
"""查询用户数据"""
filter_builder = QueryFilter(**extra_filters)
if min_age is not None or max_age is not None:
min_age = min_age or 0
max_age = max_age or 150
filter_builder.in_range('age', min_age, max_age)
if country:
filter_builder.equals('country', country)
if registration_date:
filter_builder.equals('registration_date__gte', registration_date)
return filter_builder.build()
# 构建查询
user_query = query_users(
min_age=18,
max_age=30,
country="中国",
registration_date=datetime(2023, 1, 1),
is_active=True,
email_verified=True
)
print("用户查询条件:")
for key, value in user_query.items():
print(f" {key}: {value}")
# 组合多个查询
filter1 = QueryFilter(category="电子产品", price__lte=1000)
filter2 = QueryFilter(brand="Apple", in_stock=True)
filter3 = QueryFilter(rating__gte=4.0)
combined_query = QueryFilter.combine(filter1, filter2, filter3, operator="AND")
print("\n组合查询条件:")
for key, value in combined_query.items():
print(f" {key}: {value}")
5.2 配置管理系统
在大型应用中,配置管理是一个常见需求。使用**操作符可以创建灵活的配置覆盖系统。
import json
import os
from pathlib import Path
from typing import Any, Dict
class ConfigManager:
"""多层配置管理器"""
def __init__(self):
self._config_layers = []
def add_layer(self, config: Dict[str, Any], priority: int = 0) -> None:
"""添加配置层,priority越高优先级越高"""
self._config_layers.append((priority, config))
self._config_layers.sort(key=lambda x: x[0], reverse=True)
def get(self, key: str, default: Any = None) -> Any:
"""获取配置值,按优先级合并"""
for _, config in self._config_layers:
if key in config:
return config[key]
# 支持点号访问嵌套配置
if '.' in key:
parts = key.split('.')
current = self._merge_configs()
for part in parts:
if isinstance(current, dict) and part in current:
current = current[part]
else:
return default
return current
return default
def _merge_configs(self) -> Dict[str, Any]:
"""合并所有配置层"""
if not self._config_layers:
return {}
# 从低优先级到高优先级合并
merged = {}
for _, config in reversed(self._config_layers):
merged = self._deep_merge(merged, config)
return merged
def _deep_merge(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
"""深度合并两个字典"""
result = base.copy()
for key, value in override.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = self._deep_merge(result[key], value)
else:
result[key] = value
return result
def to_dict(self) -> Dict[str, Any]:
"""获取完整合并后的配置"""
return self._merge_configs()
# 使用示例
config_manager = ConfigManager()
# 1. 默认配置(最低优先级)
default_config = {
"database": {
"host": "localhost",
"port": 5432,
"pool_size": 10
},
"logging": {
"level": "INFO",
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
},
"server": {
"port": 8000,
"debug": False
}
}
# 2. 环境特定配置(中等优先级)
environment_config = {
"database": {
"host": "prod-db.example.com",
"pool_size": 50
},
"server": {
"debug": False
}
}
# 3. 本地开发配置(最高优先级)
local_config = {
"database": {
"host": "127.0.0.1"
},
"server": {
"debug": True,
"port": 8080
},
"features": {
"experimental": True,
"beta_features": ["new_ui", "advanced_search"]
}
}
# 添加配置层
config_manager.add_layer(default_config, priority=0)
config_manager.add_layer(environment_config, priority=10)
config_manager.add_layer(local_config, priority=100)
# 获取配置值
print(f"数据库主机: {config_manager.get('database.host')}")
print(f"服务器端口: {config_manager.get('server.port')}")
print(f"调试模式: {config_manager.get('server.debug')}")
print(f"实验功能: {config_manager.get('features.experimental')}")
# 获取完整配置
full_config = config_manager.to_dict()
print(f"\n完整配置的数据库部分: {json.dumps(full_config['database'], indent=2)}")
5.3 插件系统架构
最后,我们来看一个使用*和**操作符构建的插件系统。这种架构在现代应用中非常常见,特别是需要高度可扩展性的系统。
from typing import Protocol, runtime_checkable, Any, Callable
import inspect
@runtime_checkable
class PluginProtocol(Protocol):
"""插件协议,所有插件必须实现"""
def initialize(self, **config: Any) -> None:
"""初始化插件"""
...
def process(self, *args: Any, **kwargs: Any) -> Any:
"""处理数据"""
...
def cleanup(self) -> None:
"""清理资源"""
...
class PluginManager:
"""插件管理器"""
def __init__(self):
self._plugins = {}
self._hooks = {}
def register_plugin(self, name: str, plugin: PluginProtocol) -> None:
"""注册插件"""
if not isinstance(plugin, PluginProtocol):
raise TypeError(f"插件必须实现PluginProtocol,但收到: {type(plugin)}")
self._plugins[name] = plugin
def register_hook(self, hook_name: str, plugin_name: str, method_name: str) -> None:
"""注册钩子"""
if hook_name not in self._hooks:
self._hooks[hook_name] = []
plugin = self._plugins.get(plugin_name)
if not plugin:
raise ValueError(f"未找到插件: {plugin_name}")
method = getattr(plugin, method_name, None)
if not callable(method):
raise ValueError(f"插件 {plugin_name} 没有可调用的方法 {method_name}")
self._hooks[hook_name].append(method)
def call_hook(self, hook_name: str, *args: Any, **kwargs: Any) -> list[Any]:
"""调用钩子,返回所有处理结果"""
results = []
if hook_name in self._hooks:
for hook in self._hooks[hook_name]:
try:
# 动态调用钩子,传递所有参数
result = hook(*args, **kwargs)
results.append(result)
except Exception as e:
print(f"钩子 {hook_name} 执行失败: {e}")
return results
def initialize_all(self, **global_config: Any) -> None:
"""初始化所有插件"""
for name, plugin in self._plugins.items():
# 提取插件特定的配置
plugin_config = global_config.get(name, {})
# 如果插件有initialize方法,检查它的参数
if hasattr(plugin, 'initialize'):
sig = inspect.signature(plugin.initialize)
params = list(sig.parameters.keys())
# 只传递插件需要的配置参数
filtered_config = {
k: v for k, v in plugin_config.items()
if k in params or 'kwargs' in params
}
try:
plugin.initialize(**filtered_config)
print(f"插件 {name} 初始化成功")
except Exception as e:
print(f"插件 {name} 初始化失败: {e}")
# 示例插件实现
class LoggingPlugin:
"""日志插件"""
def initialize(self, log_level: str = "INFO", log_file: str = None, **kwargs):
self.log_level = log_level
self.log_file = log_file
print(f"日志插件初始化: level={log_level}, file={log_file}")
def process(self, message: str, level: str = None, **kwargs):
level = level or self.log_level
log_entry = f"[{level}] {message}"
print(log_entry)
return log_entry
def cleanup(self):
print("日志插件清理完成")
class ValidationPlugin:
"""验证插件"""
def initialize(self, strict_mode: bool = False, **kwargs):
self.strict_mode = strict_mode
print(f"验证插件初始化: strict_mode={strict_mode}")
def validate_email(self, email: str) -> bool:
import re
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def validate_phone(self, phone: str) -> bool:
import re
# 简单的手机号验证
pattern = r'^1[3-9]\d{9}$'
return bool(re.match(pattern, phone))
def cleanup(self):
print("验证插件清理完成")
# 使用插件系统
manager = PluginManager()
# 注册插件
manager.register_plugin("logging", LoggingPlugin())
manager.register_plugin("validation", ValidationPlugin())
# 注册钩子
manager.register_hook("before_save", "logging", "process")
manager.register_hook("before_save", "validation", "validate_email")
# 初始化插件
global_config = {
"logging": {
"log_level": "DEBUG",
"log_file": "app.log"
},
"validation": {
"strict_mode": True
}
}
manager.initialize_all(**global_config)
# 调用钩子
print("\n调用 before_save 钩子:")
results = manager.call_hook(
"before_save",
"用户注册数据",
level="INFO",
email="user@example.com",
phone="13800138000"
)
print(f"钩子执行结果: {results}")
# 直接使用插件
validation_plugin = manager._plugins["validation"]
email_valid = validation_plugin.validate_email("test@example.com")
phone_valid = validation_plugin.validate_phone("13800138000")
print(f"\n直接验证:")
print(f"邮箱验证结果: {email_valid}")
print(f"手机号验证结果: {phone_valid}")
这个插件系统展示了*args和**kwargs在实际架构设计中的威力。通过动态参数传递,我们可以创建高度灵活、可扩展的系统,同时保持代码的简洁性和可维护性。每个插件可以定义自己需要的参数,插件管理器负责正确地传递这些参数,而不需要修改核心架构。
更多推荐


所有评论(0)