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

Python中类实现的方法装饰器为何必须加括号调用?

类装饰器带/不带括号的工作原理及无参实现方案

核心原理

Python装饰器本质是语法糖,两种写法的执行逻辑完全不同:

  • 写@装饰器(不带括号)时,Python会直接把被装饰的函数/方法作为第一个参数传给装饰器本身,返回值替换原函数
  • 写@装饰器()(带括号)时,Python会先执行装饰器()得到一个可调用对象,再把被装饰的函数/方法传给这个可调用对象,返回值替换原函数

你当前遇到报错的原因是:
PrintLog是类装饰器,不带括号写@PrintLog时,Python会把被装饰的baz方法直接传给PrintLog的__init__构造方法,你初始版本的__init__没有接收额外参数的逻辑,就会抛出参数不匹配的错误。带括号写@PrintLog()时,会先实例化PrintLog对象,再把baz方法传给实例的__call__方法,逻辑匹配所以能正常运行。

实现无括号无参类装饰器的方案

方案1:纯无括号无参场景适配

如果不需要给装饰器传任何参数,把接收被装饰函数的逻辑迁移到__init__,__call__负责执行包裹逻辑即可:

from functools import wraps

class PrintLog(object):
    def __init__(self, func):
        # 不带括号调用时,被装饰函数直接传入__init__
        wraps(func)(self)
        self.func = func

    def __call__(self, *args, **kwargs):
        # 调用被装饰方法时触发该逻辑
        print('I am a log')
        return self.func(*args, **kwargs)


class foo(object):
    def __init__(self, rs: str) -> None:
        self.ter = rs

    @PrintLog  # 无需加括号即可正常使用
    def baz(self) -> None:
        print('inside baz')


bar = foo('2')
print('running bar.baz()')
bar.baz()

运行结果和你之前带括号的版本完全一致。

方案2:兼容带/不带括号两种写法

如果后续可能需要给装饰器加自定义参数,可以做兼容适配,两种写法都支持:

from functools import wraps

class PrintLog(object):
    def __init__(self, func=None, *, log_msg="I am a log"):
        self.log_msg = log_msg
        if func is not None:
            # 无括号调用逻辑
            wraps(func)(self)
            self.func = func

    def __call__(self, *args, **kwargs):
        if hasattr(self, 'func'):
            # 无括号场景:直接执行包裹逻辑
            print(self.log_msg)
            return self.func(*args, **kwargs)
        else:
            # 带括号场景:接收被装饰函数
            func = args[0]
            wraps(func)(self)
            self.func = func
            return self


class foo(object):
    def __init__(self, rs: str) -> None:
        self.ter = rs

    @PrintLog  # 无括号可正常使用
    def baz(self) -> None:
        print('inside baz')
    
    @PrintLog(log_msg="custom log") # 带自定义参数也可正常使用
    def baz2(self) -> None:
        print('inside baz2')


bar = foo('2')
bar.baz()
bar.baz2()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 00:06:04