我对装饰器和闭包是全新的,我正在尝试用一个简单的例子练习。执行时会引发错误:
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'))
答案 0 :(得分:3)
"""
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)
=&GT;
f(g)(args)
答案 1 :(得分:1)
将congratulate()函数移到它正在装饰的函数(回文)之上。
答案 2 :(得分:1)
我知道我迟到了,但我想扩大。
如上所述,在这种情况下,NameError
是由您在实际创建名称之前使用名称引起的。将congratulate()
移至顶部可以解决此问题。
NameError
的公寓,你有两个与装饰/功能相关的隐含逻辑错误:第一期:
if
中的congratulate
条款始终评估为True
;当字符串是回文时,你并不完全是在祝贺。 这是因为函数对象总是求值为True
,因此if func:
形式的条件将始终执行:
def f():
pass
if f:
print("I'm true!")
# Prints: I'm true!
幸运的是,这很简单,可以通过实际调用函数if func("test string"):
第二期:
congratulate()
应该使用装饰器的方式。 装饰器是一个可调用的返回一个可调用的(callable是像函数一样的东西,类在__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
现已分配给None
,即decor
函数的返回值。
这是因为正如所指出的,@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()
时执行此功能。< / p>
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
。 (如果你还和我在一起,我推荐你。)
这里的最终问题是什么?我们对palindrome()
的参数进行了硬编码。按原样,每次拨打newFunc()
功能func
时,系统都会调用原始功能func("Test String")
,其呼叫签名为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
条款中的palindrome('Rats live on no evil star')
Congratulations, it's a palindrome!
转移到您的func。< / p>
执行后,这非常有效,让你得到你想要的结果:
{{1}}
我希望你喜欢阅读,我相信我已经完成了(现在)!