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

@classmethod与@staticmethod装饰的方法不被识别为FunctionType的原因及优雅判断方案咨询

@classmethod与@staticmethod装饰的方法不被识别为FunctionType的原因及优雅判断方案咨询

为什么@classmethod/@staticmethod不是FunctionType?

其实这个问题很好理解:@classmethod和@staticmethod本质是包装器类,而非函数的子类。当你给函数加上这些装饰器时,它们会把原始函数包裹成对应类的实例(比如classmethod对象或staticmethod对象)——这个实例内部确实持有原始函数,但它本身的类型是包装器类,而非types.FunctionType。

举个直观的例子:当你写@classmethod def cls_method(cls): ...,Python实际执行的逻辑大概是这样的:

def cls_method(cls): ...
cls_method = classmethod(cls_method)

最后存入类字典的是classmethod类的实例,不是原始的函数对象,所以isinstance(cls_method, types.FunctionType)自然返回False,但isinstance(cls_method, classmethod)会返回True。

优雅的判断方案:不用穷举装饰器类型

你担心穷举所有装饰器类型不优雅、难维护,这个顾虑完全合理。这里推荐用Python标准库inspect模块里的inspect.isroutine()函数——它专门用来判断一个对象是否是“可调用的例程”,能涵盖普通函数、类方法、静态方法、生成器函数、lambda表达式等所有你想排除的方法类型。

修改你的代码如下:

import inspect
import types

class MyMeta(type):
    def __new__(cls, clsname, bases, clsdict):
        field_names = [
            name for name, value in clsdict.items()
            # ...其他条件...
            and not inspect.isroutine(value)
        ]
        # ...后续逻辑...

inspect.isroutine()的判断逻辑很全面:只要对象是types.FunctionType、types.BuiltinFunctionType、classmethod、staticmethod,或是实现了__call__方法的生成器对象,都会返回True。这样不管未来出现什么新的标准装饰器(只要符合“可调用例程”的定义),这个判断都能生效,彻底解决穷举类型的痛点。

如果你的场景更特殊,需要直接获取包装器内部的原始函数,还可以用inspect.unwrap()函数解开多层装饰器拿到最底层的函数对象,但对你的需求来说,inspect.isroutine()已经足够简洁好用了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:24:51