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

Python装饰器工厂报错:'NoneType'对象不可调用问题排查

定时器装饰器工厂出现TypeError: 'NoneType' object is not callable的解决方法

问题重现

你实现了一个带参数的定时器装饰器工厂,但在使用时遇到错误,错误触发在访问f.__name__的位置:

import time
import typing as tp

def timer(capture_duration: bool=False) -> tp.Callable:
    def decorator(f: tp.Callable) -> tp.Callable:
        def handle_args(*args, **kwargs):
            start = time.time()
            print("Starting " + f.__name__) # 错误发生在此处
            
            ret = f(*args, **kwargs)
            end = time.time()
            
            print("Finished " + f.__name__ + f" in {end - start} seconds")
            
            if capture_duration:
                return (ret, end-start)
            else:
                return ret

        return handle_args
    return decorator

你的错误用法示例:

timer(capture_duration=True)
def some_function_to_time(args):
    pass

错误原因

你的装饰器用法完全错误:

  • timer(capture_duration=True)调用后返回的是内部的decorator函数,这个函数需要接收被装饰的目标函数作为参数,才能生成最终的包装函数。
  • 你单独执行timer(capture_duration=True)后,没有将其与some_function_to_time做任何关联,相当于这个调用没有实际作用;如果误将返回的decorator赋值给函数名(比如some_function_to_time = timer(capture_duration=True)),后续调用some_function_to_time时,会把调用参数传给decorator,导致decorator的参数f不是预期的函数(甚至为None),最终在handle_args中访问f.__name__时触发TypeError。

正确用法

方法1:使用装饰器标准@语法(推荐)

@timer(capture_duration=True)
def some_function_to_time(args):
    pass

@timer(capture_duration=True)等价于some_function_to_time = timer(capture_duration=True)(some_function_to_time),会自动将目标函数传入装饰器工厂返回的decorator函数,生成包装后的函数。

方法2:手动调用装饰器绑定函数

如果不想用@语法,可手动完成装饰过程:

def some_function_to_time(args):
    pass

# 先调用工厂获取decorator,再传入目标函数得到包装后的函数
some_function_to_time = timer(capture_duration=True)(some_function_to_time)

额外优化建议

为了保留原函数的元信息(比如__name__、__doc__),可以使用functools.wraps装饰包装函数handle_args,避免装饰后函数元信息丢失:

import time
import typing as tp
from functools import wraps

def timer(capture_duration: bool=False) -> tp.Callable:
    def decorator(f: tp.Callable) -> tp.Callable:
        @wraps(f)
        def handle_args(*args, **kwargs):
            start = time.time()
            print(f"Starting {f.__name__}")
            
            ret = f(*args, **kwargs)
            end = time.time()
            
            print(f"Finished {f.__name__} in {end - start} seconds")
            
            if capture_duration:
                return (ret, end-start)
            else:
                return ret

        return handle_args
    return decorator

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:45:38