在没有 class 实例化的情况下调用 class 方法的装饰器?

decorators to class methods called without class instantiation?

在下面的代码中,我为 class Class 方法创建了一个装饰器。我注意到即使没有创建 class 实例也会调用这个装饰器! 并且没有在 class 中调用这些方法!

有什么解释吗?

装饰师:

def deco(class_name):
    def inner_function(method):
        print("method is = {} and class is: {}".format(method.__name__,class_name.__name__))
        return method
    return inner_function

class_deco

class class_deco :
    def __init__(self):
        pass

Class:

class Class :
    def __init__(self):
       pass

    @deco(class_deco)
    def f1(self):
        pass

    @deco(class_deco)
    def f2(self):
        pass

当我 运行 脚本时:

if __name__ == "__main__":
    pass

我得到这个结果:

method is = f1 and class is: class_deco
method is = f2 and class is: class_deco

装饰器只是以下内容的语法糖

@deco(class_deco)
def f1(self):
    pass

与 -

相同
f1 = deco(class_deco)(f1)

因此,此代码会在导入模块后立即运行,就像任何其他名称声明一样,并且 f1 名称将替换为上述修饰的 f1。

如前所述,@decorator 语法只是语法糖,所以:

@somedecorator
def foo():
    pass

严格等同于

def foo():
    pass

foo = somedecorator(foo)

在你的例子中,你明确地调用了装饰器函数:

@deco(class_deco)
def f1(self):
    pass

相当于:

def f1(self):
    pass

 _effective_decorator = deco(class_deco)

 f1 = _effective_decorator(f1)

这就是为什么您的 inner_function 确实在导入时执行的原因。

采用额外参数的装饰器需要多一层嵌套,因此从技术上讲,您的装饰器应该如下所示:

def deco(cls):
    def real_deco(func):
        def inner_function(*args, **kw):
            print("method is = {} and class is: {}".format(func.__name__,cls.__name__))
            return func(*args, **kw)
        return inner_function
    return real_deco
return inner_function

但是如果重点是获取 class 方法 真正 所属的名称,这仍然是错误的 - 你应该获取 class 来自调用该方法的实例,而不是尝试在装饰器调用中对其进行硬编码(这将永远不会按预期工作,因为当您将装饰器应用于功能)。所以正确的实现应该是这样的:

def deco(func):
    # we're only supposed to use this on methods...
    def wrapper(self, *args, **kw):
        print("class {} - method {}".format(type(self).__name__, func.__name__))

    return wrapper


class Class:
    @deco
    def f1(self):
        pass

注意:这当然不会处理 class 方法和静态方法。

这里有一个演示,展示了装饰器的两种可能构建方式:


def Deco(*deco_params):
    print('In Deco', deco_params)
    def deco(func):
        print('In deco(func)')
        def inner(*args, **kwargs):
            print('In inner(*args, **kwargs)')
            return func(*args, **kwargs)
        return inner
    return deco

def deco(method):
    print('In deco(method)')
    def inner_function(*args, **kwargs):
        print("method is = {} called".format(method.__name__))
        return method(*args, **kwargs)
    return inner_function


class Class :
    def __init__(self):
       pass

    @deco
    def f1(self):
        pass

    @Deco(42)
    def f2(self):
        pass

if __name__ == "__main__":
    print('Now in Main')
    c = Class()
    c.f1()
    c.f2()

输出:

In deco(method)
In Deco (42,)
In deco(func)
Now in Main
method is = f1 called
In inner(*args, **kwargs)