这是怎么回事?...
所以,这是我的情况。我正在准备一个Web剪贴簿,以便在此脚本的某个点上,我决定使用装饰器来处理一些URL。该装饰器具有一个参数(URL),该参数应在for循环语句的帮助下动态更改,如示例脚本所示:
from functools import wraps
import logging
logging.basicConfig(level=logging.INFO)
def cycle(url):
def outer_wrapper(func):
state = 0
@wraps(func)
def inner_wrapper(**kwargs):
nonlocal state
state += 1
kwargs['url'] = url
if state == 1:
logging.info('Returning result at first execution on {} with: '
'state => {}, kwargs => {}'.format(func, state, kwargs))
return func(**kwargs)
else:
logging.info('Returning result at upcoming executions on {} with: '
'state => {}, kwargs => {}'.format(func, state, kwargs))
return func(**kwargs)
return inner_wrapper
return outer_wrapper
def print_url(url):
print('Returned from print_url function:', url)
links = ['an-url', 'another-url']
for link in links:
# Decorator
print_url = cycle(link)(print_url)
print_url()
但是,输出显示出意外的结果,至少对我而言。
INFO:root:Returning result at first execution on <function print_url at 0x000002202FD68D08> with: state => 1, kwargs => {'url': 'an-url'}
Returned from print_url function: an-url
INFO:root:Returning result at first execution on <function print_url at 0x000002202FE196A8> with: state => 1, kwargs => {'url': 'another-url'}
INFO:root:Returning result at upcoming executions on <function print_url at 0x000002202FD68D08> with: state => 2, kwargs => {'url': 'an-url'}
Returned from print_url function: an-url
装饰器保留第一次调用print_url()
函数的地址和参数。
我已经阅读了很多有关垃圾回收,弱引用和标准库的functools
的文章,但是我无法弄清楚如何“重新启动”此装饰器,以使其在迭代过程中收到新的参数。 / p>
如果可能的话,有人会提示如何解决此问题吗?
答案 0 :(得分:2)
之所以发生这种情况,是因为您正在重新装饰已经装饰的功能。
第一次迭代后:
for link in links:
# Decorator
print_url = cycle(link)(print_url)
print_url()
然后print_url
指向inner_wrapper
。您再次装饰inner_wrapper
。这与垃圾回收无关,这只是您编写此操作所要做的。
如果您删除wraps
,这一点将变得更加清楚:
def cycle(url):
def outer_wrapper(func):
state = 0
def inner_wrapper(**kwargs):
nonlocal state
state += 1
kwargs['url'] = url
if state == 1:
print('Returning result at first execution on {} with: '
'state => {}, kwargs => {}'.format(func, state, kwargs))
return func(**kwargs)
else:
print('Returning result at upcoming executions on {} with: '
'state => {}, kwargs => {}'.format(func, state, kwargs))
return func(**kwargs)
return inner_wrapper
return outer_wrapper
def print_url(url):
print('Returned from print_url function:', url)
links = ['an-url', 'another-url']
for i, link in enumerate(links):
print("Iteration :", i)
print_url = cycle(link)(print_url)
print_url()
终端输出:
Iteration : 0
Returning result at first execution on <function print_url at 0x1060892f0> with: state => 1, kwargs => {'url': 'an-url'}
Returned from print_url function: an-url
Iteration : 1
Returning result at first execution on <function cycle.<locals>.outer_wrapper.<locals>.inner_wrapper at 0x106089378> with: state => 1, kwargs => {'url': 'another-url'}
Returning result at upcoming executions on <function print_url at 0x1060892f0> with: state => 2, kwargs => {'url': 'an-url'}
Returned from print_url function: an-url
如果要访问原始函数,建议不要在循环外保留对它的引用,不要将装饰器的结果分配给该变量,例如:
original_function = print_url
for link in links:
# Decorator
print_url = cycle(link)(original_function)
print_url()
print_url = original_function
只是为了好玩,您可以在每次迭代中恢复它,从而达到以下效果:
for link in links:
# Decorator
print_url = cycle(link)(print_url)
print_url()
closure = print_url.__closure__
idx_func = print_url.__code__.co_freevars.index('func')
print_url = closure[idx_func].cell_contents
但是...那只是一个可怕的烂摊子,暴露出一堆内部细节,最好留在引擎盖下。
从根本上说,我不确定为什么这里需要装饰器,即使用该装饰器有什么好处?