如何在类中装饰方法?

Posted

技术标签:

【中文标题】如何在类中装饰方法?【英文标题】:How to decorate a method inside a class? 【发布时间】:2010-11-24 22:45:29 【问题描述】:

我试图在一个类中装饰一个方法,但是 python 抛出一个错误。我的班级是这样的:

from pageutils import formatHeader

class myPage(object):
   def __init__(self):
      self.PageName = ''

   def createPage(self):
      pageHeader = self.createHeader()

   @formatHeader   #<----- decorator
   def createHeader(self):
       return "Page Header ",self.PageName

if __name__=="__main__":
   page = myPage()
   page.PageName = 'My Page'
   page.createPage()

pageutils.py:

def formatHeader(fn):
   def wrapped():
       return '<div class="page_header">'+fn()+'</div>'
   return wrapped

Python 抛出以下错误

self.createHeader()
TypeError: wrapped() takes no arguments (1 given)

我在哪里偷懒?

【问题讨论】:

除了您要问的问题之外,createHeader() 方法返回的是一个元组,而不是一个字符串——我认为这也是不正确的。 【参考方案1】:

Python 自动将类实例作为引用传递。 (在所有实例方法中都可以看到self 参数)。

你可以这样做:

def formatHeader(fn):
    def wrapped(self=None):
        return '<div class="page_header">'+fn(self)+'</div>'
    return wrapped

【讨论】:

哦...在“createHeader”中,您将返回一个元组。改为这样做:return "Page Header " + self.PageName 从技术上讲,您也可以使用def wrapped(self),除非您还想在课堂之外使用装饰器。但随后被装饰的函数需要优雅地处理self so fnfn(self,...) 相同,或者思考 self if 的正确方法是什么? 我知道 self 是 None...?奇怪吗? @CharlieParker 我不太明白你的问题。fn 是一个变量,它包含对包装函数的引用。 fn(self, ...) 将是对该引用函数的调用。这能回答你的问题吗?【参考方案2】:

您也可以在运行时装饰方法,但不能在定义时。例如,这在您无权访问或不想编辑源代码的情况下很有用。


In [1]: class Toy():
   ...:     def __init__(self):
   ...:         return
   ...:     def shout(self, s):
   ...:         print(s)
   ...:

In [2]: def decor(fn):
   ...:     def wrapper(*args):
   ...:         print("I'm decorated")
   ...:         return fn(*args)
   ...:     return wrapper
   ...:


In [4]:

In [4]: a=Toy()

In [5]: a.shout('sa')
sa

In [6]: a.shout=decor(a.shout)

In [7]: a.shout('sa')
I'm decorated
sa

【讨论】:

【参考方案3】:

您省略了未修饰函数中存在的 self 参数(在您的情况下为 createHeader)。

def formatHeader(fn):
    from functools import wraps
    @wraps(fn)
    def wrapper(self):
        return '<div class="page_header">'+fn(self)+'</div>'
    return wrapper

如果你不确定你要装饰的函数的签名,你可以把它写得比较笼统,如下:

def formatHeader(fn):
    from functools import wraps
    @wraps(fn)
    def wrapper(*args, **kw):
        return '<div class="page_header">'+fn(*args, **kw)+'</div>'
    return wrapper

【讨论】:

+1 表示from functools import wraps。这有一个额外的好处,它从包装函数中公开了文档字符串。没有理由不使用它! from functools import wraps 还公开了函数的名称:wrapped_function.__name__ (除了它的文档)docs.python.org/2/library/functools.html#functools.wraps

以上是关于如何在类中装饰方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中装饰文本片段的最佳方法是啥?

如何在 android 中装饰编辑文本? [关闭]

在 MRO 中装饰顶部功能

如何在 Spring(Boot)中装饰 REST 响应?

Flask-RESTful中装饰器的使用

在 Python 中装饰方法签名中没有 self 的类