首页 > 解决方案 > 从具有类作为装饰器的函数装饰器返回

问题描述

这是我的一段代码(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'

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

标签: pythonclassoopdecorator

解决方案


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

@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'

推荐阅读