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

如何在Python运行时为类动态添加方法并保留其类型注解?

如何在Python运行时为类动态添加方法并保留其类型注解?

我懂你碰到的这个坑了——用setattr给类动态加完方法,调用起来一切正常,但用inspect.signature去查方法签名的时候,之前写的类型注解好像凭空消失了似的。其实这背后的原因很简单,咱们一步步来解决:

首先给你吃个定心丸:你写的类型注解根本没丢!你可以试试直接打印MyClass.dynamic_method.__annotations__,会发现x、y和返回值的注解都好好存在里面。那为什么inspect.signature看起来“没显示”?哦,因为你定义dynamic_method的时候,self参数没有加类型注解,所以signature输出里self就没有类型标注。如果你希望self的类型被识别为MyClass,那咱们手动补上就行。

下面给你几个实用的解决办法:

  • 方法一:手动补全self的类型注解
    这是最直接的方式,在把函数绑定到类之前,给函数的__annotations__字典添加self对应的类型:

    from typing import Callable
    from inspect import signature
    
    class MyClass:
        pass
    
    def dynamic_method(self, x: int, y: str) -> str:
        return f"{x} - {y}"
    
    # 补上self的类型为MyClass
    dynamic_method.__annotations__['self'] = MyClass
    setattr(MyClass, 'dynamic_method', dynamic_method)
    
    # 现在再看签名就正常了
    print(signature(MyClass.dynamic_method))
    # 输出: (self: MyClass, x: int, y: str) -> str
    
  • 方法二:用装饰器加方法?记得用@wraps保注解
    如果你习惯用装饰器的方式批量给类添加方法,可别忘用functools.wraps把原函数的元数据(包括类型注解)都保留下来,不然装饰器会把原函数的注解覆盖掉:

    from typing import Callable
    from inspect import signature
    from functools import wraps
    
    class MyClass:
        pass
    
    def add_to_myclass(func):
        @wraps(func)  # 关键!把原函数的注解、函数名等元数据都复制过来
        def wrapped(*args, **kwargs):
            return func(*args, **kwargs)
        setattr(MyClass, func.__name__, wrapped)
        return func
    
    # 用装饰器添加方法
    @add_to_myclass
    def dynamic_method(self, x: int, y: str) -> str:
        return f"{x} - {y}"
    
    print(signature(MyClass.dynamic_method))
    # 输出同样能看到完整的类型注解
    
  • 方法三:进阶操作——动态构造函数(可选)
    要是你需要更灵活地修改函数的签名或者注解,可以用types.FunctionType重新构造函数,把原函数的代码对象、注解、全局变量等都传进去。不过大部分情况下,前面两种方法就足够用了,这个属于进阶场景的备用方案:

    import types
    from inspect import signature
    
    class MyClass:
        pass
    
    def dynamic_method(self, x: int, y: str) -> str:
        return f"{x} - {y}"
    
    # 重新构造函数,保留原注解并补充self类型
    new_func = types.FunctionType(
        dynamic_method.__code__,
        dynamic_method.__globals__,
        name=dynamic_method.__name__,
        argdefs=dynamic_method.__defaults__,
        closure=dynamic_method.__closure__
    )
    new_func.__annotations__ = dynamic_method.__annotations__.copy()
    new_func.__annotations__['self'] = MyClass
    
    setattr(MyClass, 'dynamic_method', new_func)
    print(signature(MyClass.dynamic_method))
    

最后再总结一下关键点:

  1. 直接用setattr添加函数到类,原函数的类型注解是不会丢失的,只是self参数默认没类型,所以看起来像“消失”了
  2. 要让self的类型被识别,手动给函数的__annotations__加个'self'键就行
  3. 用装饰器的话,务必要加@wraps(func),不然装饰器会覆盖原函数的注解和元数据

备注:内容来源于stack exchange,提问作者Visva Dheeran Balasubramaniam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 10:59:32