【问题标题】:Declaring decorator inside a class在类中声明装饰器
【发布时间】:2016-11-26 05:35:01
【问题描述】:

我正在尝试在 Python 中使用自定义包装器/装饰器,并且我想在一个类中声明一个 inside,以便例如打印属性的快照。我已经尝试了 this question 的东西,但没有成功。


这是我想做的(注意:这段代码不起作用,我在下面解释发生了什么)

class TestWrapper():
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = 0

    def enter_exit_info(self, func):
        def wrapper(*arg, **kw):
            print '-- entering', func.__name__
            print '-- ', self.__dict__
            res = func(*arg, **kw)
            print '-- exiting', func.__name__
            print '-- ', self.__dict__
            return res
        return wrapper

    @enter_exit_info
    def add_in_c(self):
        self.c = self.a + self.b
        print self.c

    @enter_exit_info
    def mult_in_c(self):
        self.c = self.a * self.b
        print self.c


if __name__ == '__main__':
    t = TestWrapper(2, 3)
    t.add_in_c()
    t.mult_in_c()

预期的输出是:

-- entering add_in_c
-- {'a': 2, 'b': 3, 'c': 0}
5
-- exiting add_in_c
-- {'a': 2, 'b': 3, 'c': 5}
-- entering mult_in_c
-- {'a': 2, 'b': 3, 'c': 5}
6
-- exiting mult_in_c
-- {'a': 2, 'b': 3, 'c': 6}

但我这段代码给出了

Traceback (most recent call last):
  File "C:\Users\cccvag\workspace\Test\src\module2.py", line 2, in <module>
    class TestWrapper():
  File "C:\Users\cccvag\workspace\Test\src\module2.py", line 18, in     TestWrapper
    @enter_exit_info
TypeError: enter_exit_info() takes exactly 2 arguments (1 given)

如果我尝试@enter_exit_info(self)@self.enter_exit_info,我会得到NameError。我能做什么?


编辑:

我不需要首先在类中声明装饰器物理上,只要它能够访问此类实例的属性即可。我以为只有在课堂上声明才能做到这一点,Rawing's answer 证明我错了。

【问题讨论】:

  • 如果你真的想使用基于类的装饰器,你可以使用类装饰器,但这又是一个单独的独立类。

标签: python python-2.7 wrapper decorator python-decorators


【解决方案1】:

您需要明确处理self

class TestWrapper():
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = 0

    def enter_exit_info(func):
        def wrapper(self, *arg, **kw):
            print '-- entering', func.__name__
            print '-- ', self.__dict__
            res = func(self, *arg, **kw)
            print '-- exiting', func.__name__
            print '-- ', self.__dict__
            return res
        return wrapper

    @enter_exit_info
    def add_in_c(self):
        self.c = self.a + self.b
        print self.c

    @enter_exit_info
    def mult_in_c(self):
        self.c = self.a * self.b
        print self.c


if __name__ == '__main__':
    t = TestWrapper(2, 3)
    t.add_in_c()
    t.mult_in_c()

这是一个有效的python,但是在类级别有一个不是真正方法的函数有点奇怪。除非您有充分的理由这样做,否则将装饰器移动到模块级范围会更惯用。

【讨论】:

  • 同意。如果留在类定义中,我至少会通过将其名称更改为 _enter_exit_info 来表明它是私有的。
【解决方案2】:

您可以直接拦截self 参数,而不是在类中定义装饰器:

def enter_exit_info(func):
    def wrapper(self, *arg, **kw):
        print '-- entering', func.__name__
        print '-- ', self.__dict__
        res = func(self, *arg, **kw)
        print '-- exiting', func.__name__
        print '-- ', self.__dict__
        return res
    return wrapper

class TestWrapper():
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = 0

    @enter_exit_info
    def add_in_c(self):
        self.c = self.a + self.b
        print self.c

    @enter_exit_info
    def mult_in_c(self):
        self.c = self.a * self.b
        print self.c


if __name__ == '__main__':
    t = TestWrapper(2, 3)
    t.add_in_c()
    t.mult_in_c()

【讨论】:

  • 为什么这个答案被否决了?它符合我的要求,不是吗?
  • 可能是因为您的问题标题和正文明确要求“...声明一个 inside 一个类”,这个答案没有,尽管您后来编辑删除了它限制..
  • @wim 我当然希望这不是原因。我几乎不关心否决票,但是...我希望人们意识到他们可以将装饰器移到班级中,并且它也能正常工作...
  • 是的。答案很好,其实和我的建议一样(见我回答的最后一段)。
  • 当我问我的问题时,我可能有点不清楚,因此进行了编辑。这是一个很好的解决方案。
【解决方案3】:

TL;DR : 你想要的是

def enter_exit_info(func):
    def wrapper(self, *arg, **kw):
        print '-- entering', func.__name__
        print '-- ', self.__dict__
        res = func(*arg, **kw)
        print '-- exiting', func.__name__
        print '-- ', self.__dict__
        return res
    return wrapper

记住

@decorate
def myfunc():
    pass

实际上只是

的语法糖
def myfunc():
    pass
my_func = decorate(my_func)

因此,由于在您的情况下,装饰函数被装饰器的 wrapper 函数替换,因此 wrapper 函数将接收当前实例作为第一个参数。

编辑:我完全同意其他答案,即在类中定义这个装饰器是没有意义的。您不需要它来访问当前实例,因为它是作为函数的第一个参数提供的。 FWIW def 语句与在 class 语句中使用没有任何不同,它总是产生一个普通的旧 function 对象。使函数成为“方法”(并且“自动”将当前实例作为第一个参数传递)的原因是属性解析机制,参见 https://wiki.python.org/moin/FromFunctionToMethod

【讨论】:

    【解决方案4】:

    您好,您希望输出为字典格式吗? 如果你不想要字典格式的输出,你可以试试这个....

    def enter_exit_info(func):
            def wrapper(*arg, **kw):
                print '-- entering', func.__name__        
                res = func(*arg, **kw)
                print '-- exiting', func.__name__
                return res
            return wrapper
    

    那么你的输出将是

    -- entering add_in_c
    
    5
    -- exiting add_in_c
    
    -- entering mult_in_c
    
    6
    -- exiting mult_in_c
    

    【讨论】:

    • 对不起,伙计,但这不是我问题的重点。此外,答案编辑不佳。
    猜你喜欢
    • 1970-01-01
    • 2021-01-04
    • 2019-08-07
    • 2019-10-02
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    • 2017-04-04
    • 1970-01-01
    相关资源
    最近更新 更多