如何修改被装饰函数帮助文档中的参数签名?
问题描述
我有一个包含方法的类,该方法原本接收两个参数a和b,代码如下:
class Foo: def method(self, a, b): """Does something""" x, y, z = a.x, a.y, b.z return do(x, y, z)
在Python REPL中运行help(Foo)时,会显示该方法的参数为self、a、b。
我使用装饰器更新了该类,由装饰器负责解包参数,将x、y、z传入方法,而非原方法自行解包:
def unpack(fn): def _unpack(self, a, b): x, y, z = a.x, a.y, b.z return fn(self, x, y, z) return _unpack class Foo: @unpack def method(self, x, y, z): """Does something""" return do(x, y, z)
此时功能正常,但帮助文档显示method = _unpack(self, a, b),信息不够清晰。
使用functools.wraps可以修复文档显示问题,但会导致帮助文档显示方法接收x、y、z三个参数,而实际仍接收a和b两个参数,存在误导。
是否可以修改函数包装器,使其像functools.wraps一样正确获取文档字符串等属性,同时显示包装器接收的参数?
例如,即使被unpack装饰,帮助文档仍显示方法参数为self、a、b。
(我已查看functools.wraps的源码,但不确定它是否能复制函数签名。)
解决方案
要实现既保留原函数的文档字符串等元数据,又让帮助文档显示包装器的参数签名,可通过手动调整包装函数的__signature__属性,结合functools.wraps和inspect模块完成。
具体实现
导入functools和inspect模块后,修改装饰器如下:
import functools import inspect def unpack(fn): def _unpack(self, a, b): x, y, z = a.x, a.y, b.z return fn(self, x, y, z) # 复制原函数的元数据(文档、名称等) wrapped = functools.wraps(fn)(_unpack) # 手动指定函数签名为包装函数的签名 wrapped.__signature__ = inspect.signature(_unpack) return wrapped class Foo: @unpack def method(self, x, y, z): """Does something""" return do(x, y, z)
效果验证
运行help(Foo)或help(Foo.method)时,会显示:
Help on class Foo in module __main__: class Foo(builtins.object) | Methods defined here: | | method(self, a, b) | Does something | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined)
既保留了原函数的文档字符串,又正确显示了实际接收的参数self、a、b,避免了参数误导。
原理说明
functools.wraps(fn)会将原函数的__doc__、__name__等元数据复制给包装函数,解决原装饰器中文档模糊的问题;inspect.signature(_unpack)获取包装函数的参数签名,赋值给wrapped.__signature__后,help()和inspect模块会优先识别这个自定义签名,而非原函数的签名。
该方案兼容Python 3.3及以上版本,__signature__是官方支持的自定义函数签名的标准方式。
内容的提问来源于stack exchange,提问作者mipadi
相关产品推荐
相关产品推荐

