[英]How to implement a decorator function
I'm brand-new to decorators and closures, I'm trying to practice with a simple example. 我对装饰器和闭合器是全新的,我尝试通过一个简单的示例进行练习。 When executed it raises an error of:
执行时会引发以下错误:
NameError: name 'congratulate' is not defined
What do I need to change? 我需要更改什么?
"""
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'))
the essence of understanding decorator is 了解装饰器的本质是
@f
def g(args)
=> =>
f(g)(args)
将congratulate()函数移至正在装饰的函数上方(回文)。
I know I'm late to the party, but I want to expand. 我知道我参加晚会很晚,但我想扩大规模。
As noted, the NameError
in this case is caused by the fact that you use a name before you actually create one. 如前所述,在这种情况下,
NameError
是由您在实际创建名称之前使用名称引起的。 Moving congratulate()
to the top remedies this. 将
congratulate()
移到顶部可解决此问题。
NameError
you have two implicit Logic Errors relating to Decorator/Function Functionality: NameError
Appart中,您有两个与装饰器/功能功能有关的隐式逻辑错误 : First Issue: 首要问题:
if
clause in congratulate
always evaluates to True
; congratulate
您的if
子句始终求值为True
; you aren't exactly congratulating when a string is a palindrome. This is caused by the fact that function objects always evaluate to True
, so a condition of the form if func:
will always execute: 这是由于以下事实造成的:函数对象始终求值为
True
,因此条件形式为if func:
的条件将始终执行:
def f():
pass
if f:
print("I'm true!")
# Prints: I'm true!
This is thankfully trivial and can easily be fixed by actually calling the function if func("test string"):
值得庆幸的是,这是微不足道的,并且可以通过实际调用
if func("test string"):
函数来轻松解决if func("test string"):
Second Issue: 第二期:
congratulate()
the way decorators are supposed to be used. congratulate()
。 A decorator is a callable that returns a callable (callables are things like functions, classes overloaded on __call__
). 装饰器是一个可调用函数, 它返回可调用函数(可调用函数是诸如函数,在
__call__
重载的类之类的东西)。 What your 'decorator' is doing here is simply accepting a function object, evaluating if the object is True
and then printing congratulations. 您的“装饰器”在这里所做的只是简单地接受一个函数对象,评估该对象是否为
True
,然后打印出祝贺。
Worst part? 最糟糕的部分? It is also implicitly rebinding the name
palindrome
to None
. 它还隐式地将
palindrome
名称重新绑定为None
。
Again, you can see this indirect effect (+1 for rhyming) in this next snippet: 同样,您可以在下一个代码段中看到这种间接效果(押韵为+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
Cool, our function f
has been decorated, but, look what happens when we try calling our function f
: 很酷,我们的函数
f
已经过修饰,但是,看看我们尝试调用函数f
会发生什么:
f()
TypeError Traceback (most recent call last)
<ipython-input-31-0ec059b9bfe1> in <module>()
----> 1 f()
TypeError: 'NoneType' object is not callable
Yes, our function object f
has now been assigned to None
, the return value of our decor
function. 是的,我们的函数对象
f
现在已分配给了decor
函数的返回值None
。
This happens because as pointed out, the @syntax
is directly equivalent to the following: 发生这种情况是因为
@syntax
, @syntax
直接等效于以下内容:
@decor
def f(): pass
# similar to
f = decor(f) # we re-assign the name f!
Because of this we must make sure the return value of a decorator is an object that can afterwards be called again, ergo, a callable object. 因此,我们必须确保装饰器的返回值是一个可以随后再次调用的对象ergo,这是一个可调用的对象。
So what do you do? 所以你会怎么做? One option you might consider would be simply returning the function you passed:
您可能会考虑的一种选择就是简单地返回您传递的函数:
def congratulate(func):
if func("A test Phrase!"):
print('Congratulations, it\'s a palindrome!')
return func
This will guarantee that after the decorator runs on your palindrome()
function, the name palindrome
is still going to map to a callable object. 这将确保装饰器在您的
palindrome()
函数上运行之后,名称palindrome
仍将映射到可调用对象。
The problem? 问题? This turns out to be a one-time ride .
事实证明这是一次性的 。 When Python encounters your decorator and your function, it's going to execute
congratulate
once and as a result only going to execute your if
clause once . 当Python遇到您的装饰器和函数时,它将执行一次
congratulate
,因此仅执行一次 if
子句。
But you need it to run this if
every time your function is called ! 但是,
if
每次调用函数,都需要它来运行它! What can you do in order to accomplish this? 为了做到这一点,您能做什么? Return a function that executes the decorated function (so called nested function decorators).
返回执行装饰功能 (所谓的嵌套函数装饰器)的功能 。
By doing this you create a new function for the name palindrome
and this function contains your original function which you make sure is executed each time palindrome()
is called. 这样,您将为名称
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
is now a function that issues calls to your original function. newFunc
现在是一个函数,它发出对原始函数的调用。
The decoration process now assigns the palindrome
name to the newFunc
object (notice how we returned it with return newFunc
. 装饰过程现在将
palindrome
名称分配给newFunc
对象(注意我们如何通过return newFunc
返回它。
As a result, each time you execute a call of the form palindrome()
this is tranlated to newFunc()
which in turn calls func()
in its body. 结果,每次执行
palindrome()
形式的调用时,该调用都会转换为newFunc()
,后者又在其主体中调用func()
。 (If you're still with me I commend you). (如果你仍然和我在一起,我赞扬你)。
What's the final issue here? 最后的问题是什么? We've hard-coded the parameters for
func
. 我们已经对
func
的参数进行了硬编码。 As is, everytime you call palindrome()
function newFunc()
will call your original function func
with a call signature of func("Test String")
, which is not what we want, we need to be able to pass parameters. newFunc()
,每次调用palindrome()
函数newFunc()
都会使用func("Test String")
的调用签名调用原始函数func
,这不是我们想要的,我们需要能够传递参数。
What's the solution? 有什么解决方案? Thankfully, this is simple: Pass an argument to
newFunc()
which will then pass the argument to 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
Now, everytime you call palindrome('Rats live on no evil star')
this will translate to a call of newFunc('Rats live on no evil star')
which will then transfer that call to your func as func('Rats live on no evil star')
in the if
clause. 现在,每当您调用
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')
。
After execution, this works wonderfully and get's you the result you wanted: 执行后,这将非常有效,并为您带来所需的结果:
palindrome('Rats live on no evil star')
Congratulations, it's a palindrome!
I hope you enjoy reading, I believe I'm done (for now)! 希望您喜欢阅读,我相信我已经完成了!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.