类方法作为装饰器

问题描述 投票:0回答:4

我有一堂课,其中有多种方法。我想使用其中一种方法作为其他方法的装饰器。为此,我使用以下语法:

@self.action
def execute(self,req):    

其中 action 是我班级中的其他方法。但它不起作用并抛出异常

name 'self' is not defined
python decorator python-decorators
4个回答
8
投票

定义类时不能使用该类的方法;班级内没有

self
,班级也没有“烘焙”到可以访问任何班级。

您可以将方法视为函数来用作装饰器:

class SomeClass():
    def action(func):
        # decorate
        return wrapper

    @action
    def execute(self, req):
        # something

如果

action
是在基类上定义的,那么您必须通过基类引用该名称:

class Base():
    @staticmethod
    def action(func):
        # decorate
        return wrapper

class Derived(Base):
    @Base.action
    def execute(self, req):
        # something

对于 Python 2,您必须在此处将

action
设置为静态方法,否则您会得到一个未绑定的方法,该方法会抱怨您无法在没有实例作为第一个参数的情况下调用它。在 Python 3 中,您可以省略
@staticmethod
装饰器,至少对于装饰器而言是如此。

但请注意,

action
不能直接用作方法;也许那时它根本不应该成为班级的一部分。这里它不是最终用户 API 的一部分,大概这些类的实例的使用者不使用装饰器。


1
投票

需要注意的是,装饰器和被装饰函数都是未绑定方法,因此只能访问装饰器内部作用域中的 self(或者类方法的 cls),并且必须手动将装饰方法绑定到内部绑定的实例装饰器。

class A:
    x = 5
    y = 6

    def decorate(unbound):
        def _decorator(self):
            bound = unbound.__get__(self)
            return bound() * self.x
        return _decorator

    @decorate
    def func(self):
        return self.y

A().func()  # 30!!

仍在尝试了解装饰器如何被继承和覆盖。


请注意,要使装饰器正常工作,它不能绑定到实例。那就是:没有办法让这个工作

a = A()

@a.decorate
def func(*args):
    return 1

尽管这种模式比这里所要求的更常见

此时提出了一个问题:它到底是一种方法还是只是您碰巧隐藏在类中的代码?

防止装饰器被错误绑定的唯一方法是将其声明为静态方法,但它必须位于先前的超类中,因为要使用它必须绑定到尚未定义的静态类引用,只需作为自己。

class A:
    x = 1

    @staticmethod
    def decorate(unbound):
        def _decorator(self):
            bound = unbound.__get__(self)
            return bound() * self.x
        return _decorator

class B(A):

    @A.decorate
    def func(self):
        return 1

class C():
    x = 2
    @B.decorate
    def func(self):
        return 1

a = A()

class D():
    x = 3

    @a.decorate
    def func(self):
        return 1

B().func()  # 1
C().func()  # 2
D().func()  # 3
    

但是正如你所看到的,装饰器没有办法使用自己类的状态。最后一个示例中的

class A
恰好是一个具有默认
x
变量和“不相关”静态装饰器的 mixin。

那么,这还是一种方法吗?


要克服所有这些问题,您可以将同一类中的静态方法绑定到任意类型。也就是说,内置的

type
就可以了。

class A:
    x = 1

    @staticmethod
    def decorate(unbound):
        def _decorator(self):
            bound = unbound.__get__(self)
            return bound() * self.x
        return _decorator

    @decorate.__get__(type)
    def func(self):
        return 1

class B:
    x = 2

    @A.decorate
    def func(self):
        return 1

class C:
    x = 3

    @(A().decorate)  # Only for Python 3.9+, see PEP-614
    def func(self):
        return 1

A().func()  # 1
B().func()  # 2
C().func()  # 3

但这对我来说太神奇了。但仍然不是我的直觉的方法。


0
投票

在Python中,“self”作为参数传递给实例方法(第一个),“self”只是一个约定,可以将其称为“foobarbaz”(当然这很愚蠢)……重点是,从外部“self”没有定义(因为它的范围是方法)...你不能用其他类方法来装饰类方法,而是必须编写一个单独的类!


0
投票

你可以这样做。

采用@N1ngu给出的答案的第一部分。您不需要

unbound.__get__(self)

装饰器只是一个静态方法,内部函数将 self 作为参数(或使用 *args 和 arg[0] 代表 self)。

您可以对内部函数中使用的参数使用两个选项之一:(1) 使用

(self, *args, **kwargs)
或 (2) 使用
(*args, **kwargs)
并使用 args[0] 作为 self。

所以这个例子将变成:

class A:
    x = 5
    y = 6

    def decorate_with_self(func):

        # You don't actually need `*args, **kwargs`
        # But they make the function behave correctly in the general case
        def _decorator(self, *args, **kwargs):
            return func(self, *args, **kwargs) * self.x
        return _decorator
    
    def decorate_without_self(func):

        # You don't actually need `**kwargs`
        # But it makes the function behave correctly in the general case
        def _decorator(*args, **kwargs):
            # Use args[0] as self
            return func(*args, **kwargs) * args[0].x
        return _decorator

    @decorate_with_self
    def func1_to_decorate(self):
        return self.y
    
    @decorate_without_self
    def func2_to_decorate(self):
        return self.y

A().func1_to_decorate(), A().func2_to_decorate()  # returns (30, 30)
© www.soinside.com 2019 - 2024. All rights reserved.