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

为何手动添加到functools.wraps()描述符的__set_name__钩子未被调用?

为何为functools.wraps生成的装饰器添加__set_name__钩子后未被调用?

问题重现

你尝试为装饰器返回的函数添加__set_name__钩子,但该钩子从未被调用,也无报错:

import functools


def wrap(fn):
    """Decorator."""

    @functools.wraps(fn)
    def w(*args, **kwargs):
        return fn(*args, **kwargs)

    # This never gets called.
    def set_name(self, obj, name):
        print(f"inside __set_name__: {self=}, {obj=}, {name=}")

    w.__set_name__ = set_name.__get__(w)
    return w


class Foo:
    @wrap
    def foo(self):
        pass

原因分析

问题出在你对__set_name__的绑定方式上:

  • __set_name__是描述符的钩子方法,Python在类创建阶段调用它时,会自动将描述符实例(即装饰后的函数w)作为第一个参数(self)传入,无需手动绑定。
  • 你通过set_name.__get__(w)将set_name绑定成了w的绑定方法,这会导致参数传递逻辑冲突:当Python尝试调用w.__set_name__(Foo, 'foo')时,绑定方法的参数接收逻辑与Python内部的调用逻辑不匹配,最终导致钩子未被触发。
  • 此外,Python对函数类型的描述符,仅会调用其__set_name__属性中存储的未绑定可调用对象(比如普通函数),绑定方法会被跳过。

解决办法

直接将set_name函数赋值给w.set_name,去掉手动绑定的操作:

import functools


def wrap(fn):
    """Decorator."""

    @functools.wraps(fn)
    def w(*args, **kwargs):
        return fn(*args, **kwargs)

    def set_name(self, obj, name):
        print(f"inside __set_name__: {self=}, {obj=}, {name=}")

    # 直接赋值函数,无需手动绑定
    w.__set_name__ = set_name
    return w


class Foo:
    @wrap
    def foo(self):
        pass

运行修改后的代码,Foo类创建时会正常触发__set_name__钩子,输出预期内容。

补充说明

functools.wraps本身不会干扰__set_name__的调用,它仅负责将原函数的元数据(如名称、文档字符串等)复制到装饰后的函数上。你之前的误解在于手动绑定self参数,而实际上Python会自动完成描述符钩子方法的参数传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:35:36