本文简单介绍依赖注入概念,实现了一个丐版依赖注入,分析了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)