繁体   English   中英

如何实现装饰器功能

[英]How to implement a decorator function

我对装饰器和闭合器是全新的,我尝试通过一个简单的示例进行练习。 执行时会引发以下错误:

NameError: name 'congratulate' is not defined

我需要更改什么?

"""
A recursive function to check if a string is a palindrome.
"""

@congratulate
def palindrome(phrase):
    characters = [char.lower() for char in phrase if char.isalpha()]
    chars_len = len(characters)

    out1 = characters[0]
    out2 = characters[-1]

    if chars_len <= 2:
        return out1 == out2
    else:
        if out1 == out2:
            return palindrome(characters[1:-1])
        else:
            return False


def congratulate(func):
    if func:
        print('Congratulations, it\'s a palindrome!')


if __name__ == '__main__':
    print(palindrome('Rats live on no evil star'))
"""
A recursive function to check if a string is a palindrome.
"""

def congratulate(func):
    def wrapper(*argv, **kargs):
        result = func(*argv, **kargs)
        if result:
            print('Congratulations, it\'s a palindrome!')
        return result

    return wrapper

@congratulate
def palindrome(phrase):
    characters = [char.lower() for char in phrase if char.isalpha()]
    chars_len = len(characters)

    out1 = characters[0]
    out2 = characters[-1]

    if chars_len <= 2:
        return out1 == out2
    else:
        if out1 == out2:
            return palindrome(characters[1:-1])
        else:
            return False



if __name__ == '__main__':
    print(palindrome('Rats live on no evil star'))

了解装饰器的本质是

@f
def g(args)

=>

f(g)(args)

将congratulate()函数移至正在装饰的函数上方(回文)。

我知道我参加晚会很晚,但我想扩大规模。

如前所述,在这种情况下, NameError是由您在实际创建名称之前使用名称引起的。 congratulate()移到顶部可解决此问题。


NameError Appart中,您有两个与装饰器/功能功能有关的隐式逻辑错误


首要问题:

  • congratulate您的if子句始终求值为True ; 当字符串是回文符时,您并不会完全祝贺您。

这是由于以下事实造成的:函数对象始终求值为True ,因此条件形式为if func:的条件将始终执行:

def f(): 
    pass
if f: 
    print("I'm true!")  
# Prints: I'm true!

值得庆幸的是,这是微不足道的,并且可以通过实际调用if func("test string"):函数来轻松解决if func("test string"):


第二期:

  • 这里的第二个问题不那么琐碎,可能是由于装饰者可能会感到困惑。 您实际上并没有按照应该使用装饰器的方式使用congratulate()

装饰器是一个可调用函数, 返回调用函数(可调用函数是诸如函数,在__call__重载的类之类的东西)。 您的“装饰器”在这里所做的只是简单地接受一个函数对象,评估该对象是否为True ,然后打印出祝贺。

最糟糕的部分? 它还隐式地将palindrome名称重新绑定为None

同样,您可以在下一个代码段中看到这种间接效果(押韵为+1):

def decor(f):
     if f: print("Decorating can be tricky")        

@decor
def f(): 
    print("Do I even Exist afterwards?")

# When executed, this prints:
Decorating can be tricky

很酷,我们的函数f已经过修饰,但是,看看我们尝试调用函数f会发生什么:

f()
TypeError                                 Traceback (most recent call last)
<ipython-input-31-0ec059b9bfe1> in <module>()
----> 1 f()

TypeError: 'NoneType' object is not callable

是的,我们的函数对象f现在已分配给了decor函数的返回值None

发生这种情况是因为@syntax@syntax直接等效于以下内容:

@decor
def f(): pass

# similar to 
f = decor(f)  # we re-assign the name f!

因此,我们必须确保装饰器的返回值是一个可以随后再次调用的对象ergo,这是一个可调用的对象。


所以你会怎么做? 您可能会考虑的一种选择就是简单地返回您传递的函数:

def congratulate(func):
    if func("A test Phrase!"):
        print('Congratulations, it\'s a palindrome!')
    return func

这将确保装饰器在您的palindrome()函数上运行之后,名称palindrome仍将映射到可调用对象。

问题? 事实证明这是一次性的 当Python遇到您的装饰器和函数时,它将执行一次 congratulate ,因此仅执行一次 if子句。

但是, if 每次调用函数,都需要它来运行它! 为了做到这一点,您能做什么? 返回执行装饰功能 (所谓的嵌套函数装饰器)的功能

这样,您将为名称palindrome创建一个新函数,并且该函数包含您的原始函数,该函数确保每次调用palindrome()时都会执行该函数。

def congratulate(func):  # grabs your decorated function
    # a new function that uses the original decorated function
    def newFunc():
        # Use the function
        if func("Test string"):
            print('Congratulations, it\'s a palindrome!')
    # Return the function that uses the original function
    return newFunc

newFunc现在是一个函数,它发出对原始函数的调用。

装饰过程现在将palindrome名称分配给newFunc对象(注意我们如何通过return newFunc返回它。

结果,每次执行palindrome()形式的调用时,该调用都会转换为newFunc() ,后者又在其主体中调用func() (如果你仍然和我在一起,我赞扬你)。


最后的问题是什么? 我们已经对func的参数进行了硬编码。 newFunc() ,每次调用palindrome()函数newFunc()都会使用func("Test String")的调用签名调用原始函数func ,这不是我们想要的,我们需要能够传递参数。

有什么解决方案? 幸运的是,这很简单:将参数传递给newFunc() ,然后将其传递给func()

def congratulate(func):  # grabs your decorated function
    # a new function that uses the original decorated function
    # we pass the required argument <phrase>
    def newFunc(phrase):
        # Use the function
        # we use the argument <phrase>
        if func(phrase):
            print('Congratulations, it\'s a palindrome!')
    # Return the function that uses the original function
    return newFunc

现在,每当您调用palindrome('Rats live on no evil star')这都会转换为对newFunc('Rats live on no evil star')调用,该调用将以func('Rats live on no evil star') if子句中func('Rats live on no evil star')

执行后,这将非常有效,并为您带来所需的结果:

palindrome('Rats live on no evil star')
Congratulations, it's a palindrome!

希望您喜欢阅读,我相信我已经完成了!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM