本文简单介绍依赖注入概念,实现了一个丐版依赖注入,分析了Dependent如何解构handler,handler的参数如何被转化为Param子类
handler:on()所装饰的函数
什么是依赖注入
简单理解:程序运行的时候会存储一些变量,当函数需要这些变量时,自动把变量传给函数
以下面的自动同意主人邀请代码为例
from nonebot import on_request
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.event import GroupRequestEvent
from .config import auto_request_config
auto_group_request = on_request()
@auto_group_request.handle()
async def auto(bot: Bot, event: GroupRequestEvent):
if event.sub_type == "invite" and event.user_id == 10000:
await event.approve(bot)
在运行到auto_group_request时,程序发现它需要nonebot.adapters.onebot.v11.bot.Bot和nonebot.adapters.onebot.v11.event.GroupRequestEvent类型的变量,于是程序在当前环境中寻找是否有这些变量,并将变量交给handler(函数auto),触发handler逻辑
如何实现依赖注入
要实现依赖注入,需要有以下步骤
- 有一个一直在维护的环境,用于存储变量(就叫它上下文吧)
- 函数需要有注解(annotation),这样才能根据注解从上下文中找出函数需要的参数
- 通过某种方法获取函数的参数名、注解
首先看如何获取函数名和注解
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
signature = inspect.signature(call) # 可以获取参数名和类型
globalns = getattr(call, "__globals__", {})
typed_params = [
inspect.Parameter(
name=param.name,
kind=param.kind,
default=param.default,
annotation=get_typed_annotation(param, globalns), # 特殊处理str形式的annotation,如def test(a: "int", b: str)
)
for param in signature.parameters.values()
]
return inspect.Signature(typed_params)
def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any: # 借助globalns将str形式的annotation还原
annotation = param.annotation
if isinstance(annotation, str):
annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns)
return annotation
这里直接照抄NoneBot代码(位于nonebot.nonebot.dependencies.utils),get_typed_signature的作用就是获取函数的参数名和注解

这样我们可以定义Dependent,将函数拆解
class Dependent:
def __init__(self, call: Callable):
self.call = call
self.params = self.parse_params(call)
print(self.params)
@staticmethod
def parse_params(call: Callable):
fields = []
params = get_typed_signature(call).parameters.values()
for param in params:
annotation = param.annotation
fields.append({
"name": param.name,
"type": annotation
})
return fields
当我们将Dependent作为装饰器,就可以获取被装饰函数的参数信息

那么该怎么调用函数呢,我们可以定义一个类变量context,用于存储上下文;同时定义__call__,在调用时从上下文获取变量并传给函数
class Dependent:
context = {}
def __init__(self, call: Callable):
self.call = call
self.params = self.parse_params(call)
@staticmethod
def parse_params(call: Callable):
fields = []
params = get_typed_signature(call).parameters.values()
for param in params:
annotation = param.annotation
fields.append({
"name": param.name,
"type": annotation
})
return fields
def __call__(self):
self.call(**{
param["name"]: self.context[param["type"]] for param in self.params
})
这样就实现了一个简单的依赖注入,当context不同时,调用test的结果也不同

完整代码如下
import inspect
from typing import Any, Dict, Callable
from pydantic.typing import ForwardRef, evaluate_forwardref
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
signature = inspect.signature(call)
globalns = getattr(call, "__globals__", {})
typed_params = [
inspect.Parameter(
name=param.name,
kind=param.kind,
default=param.default,
annotation=get_typed_annotation(param, globalns),
)
for param in signature.parameters.values()
]
return inspect.Signature(typed_params)
def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any:
annotation = param.annotation
if isinstance(annotation, str):
annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns)
return annotation
class Dependent:
context = {}
def __init__(self, call: Callable):
self.call = call
self.params = self.parse_params(call)
@staticmethod
def parse_params(call: Callable):
fields = []
params = get_typed_signature(call).parameters.values()
for param in params:
annotation = param.annotation
fields.append({
"name": param.name,
"type": annotation
})
return fields
def __call__(self):
self.call(**{
param["name"]: self.context[param["type"]] for param in self.params
})
@Dependent
def test(a: int, b: str):
print(a, b)
pass
Dependent.context = {
int: 1,
str: "233"
}
test()
Dependent.context = {
int: 2,
str: "666"
}
test()
当然,这个代码有很多问题,比如不支持异步函数、context里可能没有对应类型的值,不支持Dependent的嵌套等
NoneBot的依赖注入
首先我们要知道,在下面这个代码中,函数会被包装为Dependent,放到Matcher.handlers列表中(具体流程会在Matcher章介绍,这里只介绍auto是如何从函数变成Denpendent)
from nonebot import on_request
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.event import GroupRequestEvent
from .config import auto_request_config
auto_group_request = on_request()
@auto_group_request.handle()
async def auto(bot: Bot, event: GroupRequestEvent):
if event.sub_type == "invite" and event.user_id == 10000:
await event.approve(bot)

Dependent的实例化之parse_params
我们看看Dependent.parse(暂时忽视中间的[Any])在干什么(代码位于nonebot.dependencies.__init__)
@dataclass(frozen=True)
class Dependent(Generic[R]):
...
@classmethod
def parse(
cls,
*,
call: _DependentCallable[R],
parameterless: Optional[Iterable[Any]] = None,
allow_types: Iterable[Type[Param]],
) -> "Dependent[R]":
allow_types = tuple(allow_types)
params = cls.parse_params(call, allow_types)
parameterless_params = (
tuple()
if parameterless is None
else cls.parse_parameterless(tuple(parameterless), allow_types)
)
return cls(call, params, parameterless_params)
它接收三个参数
- call就是handler(如上文的auto),类型为_DependentCallable(还是先忽略[R],本文后会提及),等于Union[Callable[..., T], Callable[..., Awaitable[T]]],也就是说同步函数异步函数都支持
- parameterless(暂时忽略,本文后会提及)
- allow_types,类型为Iterable[Type[Param]],是为了实现嵌套依赖注入。当参数类型是Param的子类时,代码不会和之前一样傻傻的去context里找这个类型的变量,而是执行这个Param;对于Param的参数,会根据注解类型去context里找这个类型的变量,如果注解类型是Param的子类,那就执行这个Param...这就实现了嵌套dependent
例如下面这个代码

Depends(get_msg)是nonebot.internal.params.DependsInner类型,最终会变成nonebot.internal.params.DependParam类型(后文会介绍是怎么变的),而DependParam是nonebot.dependencies.Param子类,所以get_msg函数会被执行,由于它是Param的子类,有类似dependent的性质,能够从context中找到类型为PrivateMessageEvent的变量赋值给event,最终将返回值event.get_plaintext()赋值给msg
现在你应该对NoneBot插件里那些语法有些模糊理解了,至于Param是怎么实现的,我们很快就会介绍到
接下来看parse内部代码,由于allow_types和parameterless_params暂时忽略,我们只看params = cls.parse_params(call, allow_types)
cls.parse_params和前面那个丐版依赖注入的parse_params功能相似
@dataclass(frozen=True)
class Dependent(Generic[R]):
@staticmethod
def parse_params(
call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
) -> Tuple[ModelField]:
fields: List[ModelField] = []
params = get_typed_signature(call).parameters.values()
for param in params:
default_value = Required
if param.default != param.empty:
default_value = param.default
if isinstance(default_value, Param):
field_info = default_value
else:
for allow_type in allow_types:
if field_info := allow_type._check_param(param, allow_types):
break
else:
raise ValueError(
f"Unknown parameter {param.name} for function {call} with type {param.annotation}"
)
default_value = field_info.default
annotation: Any = Any
required = default_value == Required
if param.annotation != param.empty:
annotation = param.annotation
annotation = get_annotation_from_field_info(
annotation, field_info, param.name
)
fields.append(
ModelField(
name=param.name,
type_=annotation,
class_validators=None,
model_config=CustomConfig,
default=None if required else default_value,
required=required,
field_info=field_info,
)
)
return tuple(fields)
这段代码信息量很大,我们来一段段分析
from nonebot import on_message
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.params import Depends, DefaultParam
from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
from .config import auto_request_config
test = on_message()
def get_msg(event: PrivateMessageEvent):
return event.get_plaintext()
@test.handle()
async def _(
bot: Bot,
event: PrivateMessageEvent,
msg: str = Depends(get_msg),
flag: bool = DefaultParam(True) # NoneBot中默认值需要这样写
):
pass
假设handler如上图所示,则
def parse_params(
call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
) -> Tuple[ModelField]:
fields: List[ModelField] = [] # 用于存储handler的参数的类型注解和默认值,相当于丐版依赖注入的fields类似
params = get_typed_signature(call).parameters.values() # 获取handler的参数的类型注解和默认值
```
params的结果
odict_values([
<Parameter "bot: Bot">,
<Parameter "event: PrivateMessageEvent">,
<Parameter "msg: str = Depends">,
<Parameter "flag: str = DefaultParam">
])
```
for param in params: # 遍历已经拆分出的参数列表,结果是
```
如果没有默认值,如bot,event,就将默认值default_value设为Required
msg有默认值,default_value就是Depends;flag有默认值,default_value就是DefaultParam
```
default_value = Required
if param.default != param.empty:
default_value = param.default
if isinstance(default_value, Param):
```
如果default_value就是Param子类,将Param作为field_info
这里只有DefaultParam是Param子类,所以参数flag的field_info为DefaultParam
```
field_info = default_value
else:
```
对于bot、event、msg,会将其转换为Param子类
这也是为什么说matcher中任何的参数最终都是Param
```
for allow_type in allow_types:
if field_info := allow_type._check_param(param, allow_types):
break
else:
raise ValueError(
f"Unknown parameter {param.name} for function {call} with type {param.annotation}"
)
bot、event、msg的field_info为什么是Param的子类?首先要知道allow_types是什么
不管你是on_message,on_regex还是on什么,handler最终都是在nonebot.internal.matcher.Matcher.new被转化为dependent,传入的allow_types为cls.HANDLER_PARAM_TYPES

也就是

这些Param都位于nonebot.internal.params
class DependParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["DependParam"]: if isinstance(param.default, DependsInner): dependency: T_Handler if param.default.dependency is None: assert param.annotation is not param.empty, "Dependency cannot be empty" dependency = param.annotation else: dependency = param.default.dependency sub_dependent = Dependent[Any].parse( call=dependency, allow_types=allow_types, ) return cls( Required, use_cache=param.default.use_cache, dependent=sub_dependent ) ...
class BotParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["BotParam"]: from nonebot.adapters import Bot if param.default == param.empty: if generic_check_issubclass(param.annotation, Bot): checker: Optional[ModelField] = None if param.annotation is not Bot: checker = ModelField( name=param.name, type_=param.annotation, class_validators=None, model_config=CustomConfig, default=None, required=True, ) return cls(Required, checker=checker) elif param.annotation == param.empty and param.name == "bot": return cls(Required) ...
class EventParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["EventParam"]: from nonebot.adapters import Event if param.default == param.empty: if generic_check_issubclass(param.annotation, Event): checker: Optional[ModelField] = None if param.annotation is not Event: checker = ModelField( name=param.name, type_=param.annotation, class_validators=None, model_config=CustomConfig, default=None, required=True, ) return cls(Required, checker=checker) elif param.annotation == param.empty and param.name == "event": return cls(Required) ...
class StateParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["StateParam"]: if param.default == param.empty: if param.annotation is T_State: return cls(Required) elif param.annotation == param.empty and param.name == "state": return cls(Required) ...
class ArgParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["ArgParam"]: if isinstance(param.default, ArgInner): return cls( Required, key=param.default.key or param.name, type=param.default.type ) ...
class MatcherParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["MatcherParam"]: from nonebot.matcher import Matcher if generic_check_issubclass(param.annotation, Matcher) or ( param.annotation == param.empty and param.name == "matcher" ): return cls(Required) ...
class DefaultParam(Param): ... @classmethod def _check_param( cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...] ) -> Optional["DefaultParam"]: if param.default != param.empty: return cls(param.default) ...
容易看出,msg、bot、event会依次被DependParam._check_param、BotParam._check_param、EventParam._check_param转化为DependParam、BotParam、EventParam
所以说“handler中任何的参数最终都是Param”,指的是handler中任何的field_info最终都是Param
接着分析parse_params
def parse_params(
call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
) -> Tuple[ModelField]:
fields: List[ModelField] = []
for param in params:
```
经过处理,field_info全部变成了Param子类
bot的field_info: BotParam
event的field_info: EventParam
msg的field_info: DependParam
flag的field_info: DefaultParam
```
...
default_value = field_info.default # 除了DefaultParam可以设置field_info.default,如本例中为True,其它Param子类的field_info.default都是Required
annotation: Any = Any
required = default_value == Required # 除了DefaultParam外其它Param子类的required都是True
if param.annotation != param.empty:
# param的注解不为空的话,annotation就等于param.annotation
annotation = param.annotation
```
bot的annotation: Bot
event的annotation: Event
msg的annotation: Depends
flag的annotation: DefaultParam
```
annotation = get_annotation_from_field_info(
annotation, field_info, param.name
) # 貌似是无效代码,annotation值不改变
fields.append(
ModelField(
name=param.name, # 参数名
type_=annotation, # 参数类型注解
class_validators=None, # 校验钩子,这里只是用ModelField简单存储信息,不需要
model_config=CustomConfig, # 见nonebot.dependencies.\__init__.CustomConfig,允许任意类型,不重要
default=None if required else default_value, # 类型注解为DefaultParam的参数才有,如这个例子中flag的default是True (flag:bool=DefaultParam(True)),其它参数则均为None
required=required, # 只有类型注解为DefaultParam的参数才为False,其它参数的required都是True
field_info=field_info, # 参数转化后的Param子类
)
) # 将函数参数信息存到fields列表
return tuple(fields) # 转为元组
至此我们就弄清了handler的参数是如何解构的
Dependent的实例化之parse_parameterless
接下来分析之前忽略的parameterless参数
@dataclass(frozen=True)
class Dependent(Generic[R]):
@classmethod
def parse(
cls,
*,
call: _DependentCallable[R],
parameterless: Optional[Iterable[Any]] = None,
allow_types: Iterable[Type[Param]],
) -> "Dependent[R]":
allow_types = tuple(allow_types)
params = cls.parse_params(call, allow_types) # 已经分析完了
parameterless_params = (
tuple()
if parameterless is None
else cls.parse_parameterless(tuple(parameterless), allow_types)
) # 接下来分析
return cls(call, params, parameterless_params)
易知parameterless_params默认是空元组,如果parameterless不为None,进入cls.parse_parameterless
@dataclass(frozen=True)
class Dependent(Generic[R]):
...
@staticmethod
def parse_parameterless(
parameterless: Tuple[Any, ...], allow_types: Tuple[Type[Param], ...]
) -> Tuple[Param, ...]:
parameterless_params: List[Param] = []
for value in parameterless:
for allow_type in allow_types:
if param := allow_type._check_parameterless(value, allow_types):
break
else:
raise ValueError(f"Unknown parameterless {value}")
parameterless_params.append(param)
return tuple(parameterless_params)
和parse_params中的Param化如出一辙,只不过用的是_check_parameterless
返回值是parameterless参数Param化后的元组
目前只有DependParam支持parameterless
如
def parse_int(key: str):
async def _key_parser(
matcher: Matcher, state: T_State, input: int | Message = Arg(key)
):
if isinstance(input, int):
return
plaintext = input.extract_plain_text()
if not plaintext.isdigit():
await matcher.reject_arg(key, "请只输入数字,不然我没法理解呢!")
state[key] = int(plaintext)
return _key_parser
@history_cmd.got(
"month",
prompt="你请输入你要查询的月份",
parameterless=[Depends(parse_int("month"))], # 最终parameterless_params=(DependParm,)
)
async def history_handle_group_message(
bot: Bot,
event: GroupMessageEvent,
year: int = Arg(),
month: int = Arg(),
day: int = Arg(),
):
pass
Dependent的实例化
@dataclass(frozen=True)
class Dependent(Generic[R]):
call: _DependentCallable[R]
params: Tuple[ModelField] = field(default_factory=tuple)
parameterless: Tuple[Param] = field(default_factory=tuple)
...
@classmethod
def parse(
cls,
*,
call: _DependentCallable[R],
parameterless: Optional[Iterable[Any]] = None,
allow_types: Iterable[Type[Param]],
) -> "Dependent[R]":
allow_types = tuple(allow_types) # 已经分析完了
params = cls.parse_params(call, allow_types) # 已经分析完了
parameterless_params = (
tuple()
if parameterless is None
else cls.parse_parameterless(tuple(parameterless), allow_types)
) # 已经分析完了
return cls(call, params, parameterless_params) #接下来分析
看起来Dependent没有__init__,为什么能用cls()来实例化?
因为Dependent被dataclass装饰了,cls(call, params, parameterless_params)会将call、params、parameterless_params赋值给cls.call、cls.params、cls.parameterless_params
至此Dependent实例化就分析完了
Param子类的实例化
前面简单介绍了Param,这节将详细介绍Param子类实例化
DependParam
class DependsInner:
def __init__(
self,
dependency: Optional[T_Handler] = None,
*,
use_cache: bool = True,
) -> None:
self.dependency = dependency
self.use_cache = use_cache
def __repr__(self) -> str:
dep = get_name(self.dependency)
cache = "" if self.use_cache else ", use_cache=False"
return f"DependsInner({dep}{cache})"
def Depends(
dependency: Optional[T_Handler] = None,
*,
use_cache: bool = True,
) -> Any:
return DependsInner(dependency, use_cache=use_cache)
class DependParam(Param):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["DependParam"]:
if isinstance(param.default, DependsInner): # 只有DependsInner会被转化为DependParam
dependency: T_Handler
```
param.default是DependsInner类型,param.default.dependency就是Depends的参数,例如前文的get_msg
所以一般来说param.default.dependency不是None,除非你在使用Depends时没有传入函数,如async def(msg=Depends())
```
if param.default.dependency is None: # 如async def(msg:str=Depends())或async def(msg=Depends())
assert param.annotation is not param.empty, "Dependency cannot be empty" # handler参数没有类型注释且Depends没有传参就会报错,如async def(msg=Depends())
dependency = param.annotation # 形如async def(msg:get_msg=Depends()),等价于async def(msg=Depends(get_msg))
else:
dependency = param.default.dependency # 例如async def(msg=Depends(get_msg)),dependency就是get_msg
```
省流:async def(msg:get_msg=Depends())或者async def(msg=Depends(get_msg))都是正确写法。对于第二种写法,msg的类型注释不会影响代码运行
```
sub_dependent = Dependent[Any].parse(
call=dependency,
allow_types=allow_types,
) # dependency转化为Dependent,sub_dependent的allow_types与handler生成的主Dependent相同,意味着依赖注入可以递归调用
return cls(
Required, use_cache=param.default.use_cache, dependent=sub_dependent
) # DependsInner转化为DependParam,
@classmethod
def _check_parameterless(
cls, value: Any, allow_types: Tuple[Type[Param], ...]
) -> Optional["Param"]:
if isinstance(value, DependsInner): # 和_check_param类似
assert value.dependency, "Dependency cannot be empty"
dependent = Dependent[Any].parse(
call=value.dependency, allow_types=allow_types
)
return cls(Required, use_cache=value.use_cache, dependent=dependent) # DependsInner转化为DependParam
# 可以看出DependParam要么是DependParam要么是DependParameterless
BotParam
class BotParam(Param):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["BotParam"]:
from nonebot.adapters import Bot
if param.default == param.empty: # 有两种用法
if generic_check_issubclass(param.annotation, Bot): # async def(xxx:Bot)这一种用法,不能接默认值,参数名可以随便取,Bot需要是nonebot.internal.adapter.bot.Bot本身或它的子类
checker: Optional[ModelField] = None
if param.annotation is not Bot:
```
如果Bot不是nonebot.internal.adapter.bot.Bot,说明Bot是它的子类
这说明bot是协议的bot而不是基类bot,于是记录协议的bot的信息
只有协议bot消息才能通过check(将在下一节介绍)
例如handler规定bot是OneBot V11的bot,那只有OneBot V11的事件会触发这个handler,
那为什么基类Bot不需要check呢?这样的话如果handler的bot是基类bot,那么这个插件是可以多平台运行的
```
checker = ModelField(
name=param.name,
type_=param.annotation,
class_validators=None,
model_config=CustomConfig,
default=None,
required=True,
)
return cls(Required, checker=checker) # 将Bot转化为BotParam。基类Bot的checker为None,任何消息都能触发handler,协议Bot只有收到对应协议的消息才能触发handler
elif param.annotation == param.empty and param.name == "bot": # async def(bot)是另一种用法,参数名必须是bot,且不能有类型注释,这表明handler是可以多平台运行的
return cls(Required) # 基类Bot的BotParam
EventParam
class EventParam(Param): # 和BotParam如出一辙
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["EventParam"]:
from nonebot.adapters import Event
if param.default == param.empty:
if generic_check_issubclass(param.annotation, Event):
checker: Optional[ModelField] = None
if param.annotation is not Event:
checker = ModelField(
name=param.name,
type_=param.annotation,
class_validators=None,
model_config=CustomConfig,
default=None,
required=True,
)
return cls(Required, checker=checker) # 将Event转化为EventParam。基类Event的checker为None,任何事件都能触发handler,协议Event只有收到对应协议的event才能触发handler
elif param.annotation == param.empty and param.name == "event":
return cls(Required) # 基类Event转化的EventParam,可以多平台触发
StateParam
class StateParam(Param):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["StateParam"]:
if param.default == param.empty: # 不能有默认值
if param.annotation is T_State: # 用法一:async def (xxx:T_State),参数名随便取,类型注释必须是nonebot.typing.T_State
return cls(Required)
elif param.annotation == param.empty and param.name == "state": # 用法二:async def (state),参数名必须是state且不能有类型注释
return cls(Required)
MatcherParam
class MatcherParam(Param):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["MatcherParam"]:
from nonebot.matcher import Matcher
if generic_check_issubclass(param.annotation, Matcher) or (
param.annotation == param.empty and param.name == "matcher"
):
```
async def (xxx:Matcher)参数名随便取,类型必须是nonebot.matcher.Matcher
或
async def (matcher)参数名必须是matcher,且没有类型注释
```
return cls(Required)
```
为什么要MatcherParam呢,我自己就在handler里能不知道matcher是什么?
其实MatcherParam主要是在parameterless,使得依赖注入对不同handler可以复用
如前文parameterless=[Depends(parse_int("month"))]这种用法
```
ArgParam
class ArgInner:
def __init__(
self, key: Optional[str], type: Literal["message", "str", "plaintext"]
) -> None:
self.key = key
self.type = type
def __repr__(self) -> str:
return f"ArgInner(key={self.key!r}, type={self.type!r})"
def Arg(key: Optional[str] = None) -> Any:
return ArgInner(key, "message")
def ArgStr(key: Optional[str] = None) -> str:
return ArgInner(key, "str") # type: ignore
def ArgPlainText(key: Optional[str] = None) -> str:
return ArgInner(key, "plaintext") # type: ignore
class ArgParam(Param): # 暂时可能看不懂用法,到下一节介绍Matcher的运行时再研究
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["ArgParam"]:
if isinstance(param.default, ArgInner):
return cls(
Required, key=param.default.key or param.name, type=param.default.type
)
ExceptionParam
class DefaultParam(Param):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["ExceptionParam"]:
if generic_check_issubclass(param.annotation, Exception) or (
param.annotation == param.empty and param.name == "exception"
):
```
可以用Exception作为类型注释或将参数命名为exception
```
return cls(Required)
DefaultParam
class DefaultParam(Param):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
) -> Optional["DefaultParam"]:
```
由于函数被拆解了,python自身的默认值写法无法使用,需要借助DefaultParam
至于为什么能实现默认值,见前文关于Dependent.parse的分析
```
if param.default != param.empty:
return cls(param.default)