如何在不丢失类型信息的情况下包装Python函数?
问题描述
我想给调用外部库的Python函数添加一些高级逻辑,比如:
class Example(): def my_func(self, *args, **kwargs): return library.func(*args, **kwargs)
根据外部库的执行结果,可能需要执行不同操作,比如增加重试逻辑:
import time class Example(): def my_func(self, *args, **kwargs): try: return library.func(*args, **kwargs) except LibraryRateLimitError: # 简化的退避重试逻辑 time.sleep(2) return self.my_func(*args, **kwargs)
但问题是,这个新函数丢失了所有类型信息。由于library.func结构复杂,有数十个参数,我不想手动标注类型。有没有办法静态告知类型分析器,这个包装后的函数和底层函数接收完全相同的参数?
解决方案
方法1:使用functools.wraps
functools.wraps会复制原函数的元数据(包括类型提示)到包装函数,主流类型检查器(如mypy、Pyright)和IDE都能识别这种方式:
import functools import time import library class Example(): @functools.wraps(library.func) def my_func(self, *args, **kwargs): try: return library.func(*args, **kwargs) except library.LibraryRateLimitError: time.sleep(2) return self.my_func(*args, **kwargs)
注意:如果原函数是实例方法,而包装函数属于另一个类,可能需要额外适配,但针对普通函数或同层级类方法,该方法足够实用。
方法2:使用typing.ParamSpec和ReturnType(Python 3.10+)
要实现更精准的类型传递,可以用ParamSpec定义原函数的参数规范,ReturnType提取原函数的返回类型,让类型检查器完全复用原函数的类型信息:
import time import library from typing import ParamSpec, ReturnType P = ParamSpec('P') class Example(): def my_func(self, *args: P.args, **kwargs: P.kwargs) -> ReturnType[library.func]: try: return library.func(*args, **kwargs) except library.LibraryRateLimitError: time.sleep(2) return self.my_func(*args, **kwargs)
这种方式灵活性更高,即便你需要对参数做微调,也能保留大部分类型约束。
方法3:手动设置__signature__
如果前两种方法无法满足类型检查器或IDE的识别需求,可以用inspect.signature获取原函数的签名,直接赋值给包装函数的__signature__属性:
import inspect import time import library class Example(): def my_func(self, *args, **kwargs): try: return library.func(*args, **kwargs) except library.LibraryRateLimitError: time.sleep(2) return self.my_func(*args, **kwargs) # 复制原函数的签名信息 Example.my_func.__signature__ = inspect.signature(library.func)
该方法能让IDE(如PyCharm)正确提示参数,类型检查器通常也能兼容。
内容的提问来源于stack exchange,提问作者Alexander Craggs
相关产品推荐
相关产品推荐

