You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Python函数动态分配函数签名?

动态为函数分配签名的装饰器实现方案

我希望创建一个装饰器,能为被装饰函数动态分配函数签名,示例代码如下:

import inspect

signature = inspect.Signature([
    inspect.Parameter('x', kind=inspect.Parameter.KEYWORD_ONLY, default=None),
    inspect.Parameter('y', kind=inspect.Parameter.KEYWORD_ONLY, default=None),
    inspect.Parameter('z', kind=inspect.Parameter.KEYWORD_ONLY, default=None)])

def with_signature(signature: inspect.Signature):
    def _decor(f):
        def _wrapper():
            ...
        return _wrapper
    return _decor


@with_signature(signature)
def fun():
    return [x, y, z]

此时fun应具备与def fun(x=None, y=None, z=None)定义一致的签名,同时希望支持签名中的类型提示。该如何实现?

我的尝试1:粗糙的实现方案

我想出了一个略显粗糙的方案:

import functools
import types

def with_signature(signature: inspect.Signature):

    def _decor(f):
        
        @functools.wraps(f)
        def _wrapper(**kwargs):
            # 校验参数是否符合签名
            signature.bind(**kwargs)
        
            # 以kwargs作为全局变量创建新函数并执行
            _f = types.FunctionType(
                f.__code__,
                kwargs
            )

            return _f()
        return _wrapper
    return _decor

使用方式如下:

signature = inspect.Signature([
    inspect.Parameter('x', kind=inspect.Parameter.KEYWORD_ONLY, default=None),
    inspect.Parameter('y', kind=inspect.Parameter.KEYWORD_ONLY, default=None),
    inspect.Parameter('z', kind=inspect.Parameter.KEYWORD_ONLY, default=None)])

@with_signature(signature)
def fun():
    return [x, y, z]

print(fun(x=1, z=3, y=2))  # 输出 [1,2,3]

该方案通过signature.bind校验**kwargs,再以**kwargs作为全局变量创建函数,但无法真正修改被装饰函数的签名,亟待优化。

我的尝试2:更符合Python风格的替代方案

我发现这种实现方式不太符合Python风格,实际需求是提供明确的函数接口,因此使用**kwargs结合typing_extension.Unpack与typing.TypedDict或许是更佳方案:

from typing import TypedDict
from typing_extensions import Unpack

class Signature(TypedDict):
    x: int = 0
    y: int = 0
    z: int = 0

def fun(**kwargs: Unpack[Signature]):
    x = kwargs.get('x', 0)
    y = kwargs.get('y', 0)
    z = kwargs.get('z', 0)
    return [x, y, z]

目前我会采用该方案,但仍希望得到原问题的解决方案建议。


解决方案建议

要实现真正修改函数签名的装饰器,核心是为包装后的函数设置__signature__属性,同时处理参数绑定和类型提示,具体实现如下:

import inspect
import functools

def with_signature(signature: inspect.Signature):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 绑定参数到签名,自动处理默认值和参数校验
            bound_args = signature.bind(*args, **kwargs)
            bound_args.apply_defaults()
            # 将绑定后的参数作为关键字参数传入原函数
            return func(**bound_args.arguments)
        
        # 为包装函数设置签名,让inspect和类型检查工具识别
        wrapper.__signature__ = signature
        # 同步类型提示(如果原函数有)
        if hasattr(func, '__annotations__'):
            # 合并签名中的参数类型提示到包装函数
            for param in signature.parameters.values():
                if param.annotation is not inspect.Parameter.empty:
                    wrapper.__annotations__[param.name] = param.annotation
            # 保留原函数的返回值类型
            if 'return' in func.__annotations__:
                wrapper.__annotations__['return'] = func.__annotations__['return']
        return wrapper
    return decorator

使用示例

# 定义带类型提示的签名
signature = inspect.Signature([
    inspect.Parameter('x', kind=inspect.Parameter.KEYWORD_ONLY, default=None, annotation=int),
    inspect.Parameter('y', kind=inspect.Parameter.KEYWORD_ONLY, default=None, annotation=int),
    inspect.Parameter('z', kind=inspect.Parameter.KEYWORD_ONLY, default=None, annotation=int)
], return_annotation=list[int])

@with_signature(signature)
def fun(**kwargs):
    return [kwargs['x'], kwargs['y'], kwargs['z']]

# 验证签名
print(inspect.signature(fun))  # 输出 (x: int = None, y: int = None, z: int = None) -> list[int]
# 调用测试
print(fun(x=1, y=2, z=3))  # 输出 [1,2,3]

方案说明

  1. 真实修改签名:通过设置wrapper.__signature__ = signature,让inspect模块、IDE类型检查工具都能识别到新的函数签名。
  2. 参数校验与绑定:使用signature.bind和apply_defaults自动处理参数校验、默认值填充,避免手动处理**kwargs的繁琐。
  3. 类型提示支持:同步签名中的参数类型和原函数的返回值类型到包装函数,确保类型检查工具正常工作。
  4. 兼容原函数逻辑:原函数可以通过**kwargs接收所有参数,或者根据需要直接使用参数名(如果装饰器绑定后传入的是关键字参数)。

如果希望原函数不需要写**kwargs,可以结合exec动态生成适配层,但这种方式可读性较差,不如上述方案稳妥。

内容的提问来源于stack exchange,提问作者lupl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:37:55