从类作为装饰器的函数装饰器返回

时间:2021-03-28 07:20:19

标签: python class oop decorator

这是我的一段代码(Python 3.9.2)

class My_style:
    #self.fn = fn
    #fn= greet()
    def __init__(self,s):
        self.s = s
    def __call__(self,fn):
        self.fn = fn()
        if self.s == "upper":
            return self.fn.upper()
        elif self.s == "bold":
            return "<b>" + self.fn +  "</b>"
#@My_style("bold")
def greet():
    return "Hello World"
decorator = My_style("upper")
greet = decorator(greet)  ## This returns the string object instead of function object greet()
greet
'HELLO WORLD'

你得到的不是函数greet(),而是作为greet的字符串。 我不明白为什么它返回一个字符串。

我期待上面的输出与greet()

所以上面的装饰器工作得很好,如果我只是说greet(作为一个字符串)但我希望它工作是greet()(函数调用)

我错过了什么?

添加了它返回一个没有装饰器包装器的函数作为答案中提到的返回(但没有参数化在“上”或“粗体”之间进行选择)

class ToUpper:
    def __init__(self, fn): # Decorator
        self.fn = fn
    
    def __call__(self):  # Wrapper
        return self.fn().upper()

def greet():  
    return "Hello world"
greet = ToUpper(greet) # This returns greet() instead of string
greet()
'HELLO WORLD'

可能参数化调用需要它显式返回函数(在调用方法中)?

1 个答案:

答案 0 :(得分:3)

如果我们重写可能更容易理解发生了什么

@My_style("bold")
def greet():
    return "Hello World"

没有装饰器语法糖:

def greet():
    return "Hello World"

decorator = My_style("bold")
greet = decorator(greet)

您可以看到,第一步是创建可调用的装饰器(__init__ 在那时被执行)。

然后我们称之为装饰器。这就是 __call__ 被执行的时候,它应该返回新修饰的函数,我们将为其提供原始函数的名称 (greet)。现在装饰器已经完成了它的工作,不会再参与任何事情了。

调用 greet 然后调用这个装饰函数(而不是装饰器的 __call__ 方法!)。

因此,代码的更新版本,其中 __call__ 创建并返回装饰函数:

class My_style:
    #self.fn = fn
    #fn= greet()
    def __init__(self,s):
        self.s = s
    
    def __call__(self,fn):
        def decorated():
            if self.s == "upper":
                return fn().upper()
            elif self.s == "bold":
                return "<b>" + fn() +  "</b>"
        return decorated    
        
@My_style("bold")
def greet():
    return "Hello World"

greet()
# '<b>Hello World</b>'

@My_style("upper")
def shout():
    return "Hello World"

shout()
# 'HELLO WORLD'