Python 有没有一种方法可以;“解决问题”;装饰器的保留状态?

Python 有没有一种方法可以;“解决问题”;装饰器的保留状态?,python,python-3.x,decorator,Python,Python 3.x,Decorator,发生了什么事 这就是我的情况。我正在详细说明一个web摘要,因此在这个脚本的某个时候,我决定使用一个装饰器来处理一些URL。此decorator有一个参数(URL),其中应在for循环语句的帮助下动态更改,如示例脚本所示: from functools import wraps import logging logging.basicConfig(level=logging.INFO) def cycle(url): def outer_wrapper(func):

发生了什么事

这就是我的情况。我正在详细说明一个web摘要,因此在这个脚本的某个时候,我决定使用一个装饰器来处理一些URL。此decorator有一个参数(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:with:state=>1,kwargs=>{'url':'anurl'}上第一次执行时返回结果
从print_url函数返回:一个url
INFO:root:在上第一次执行时返回结果,其中:state=>1,kwargs=>{'url':'other url'}
INFO:root:在上即将执行时返回结果,其中:state=>2,kwargs=>{'url':'anurl'}
从print_url函数返回:一个url
装饰程序保留第一次调用
print\u url()
函数时的地址和参数。 我读过很多关于垃圾收集、弱引用和标准库的
functools
的文章,但我不知道如何“重新启动”这个装饰器,以便它在迭代过程中接收新参数


如果可能的话,有没有人知道如何解决这个问题?

这是因为您正在重新装饰您已经装饰过的函数

在第一次迭代之后:

for link in links:
    # Decorator
    print_url = cycle(link)(print_url)
    print_url()
然后,
print\u url
指的是
internal\u wrapper
。再次装饰
内包装。这与垃圾收集无关,这只是你写这篇文章的目的

如果删除
包装
,这一点会更加清楚:

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()
和终端输出:

迭代:0
在上第一次执行时返回的结果为:state=>1,kwargs=>{'url':'an url'}
从print_url函数返回:一个url
迭代:1
在上第一次执行时返回的结果为:state=>1,kwargs=>{'url':'other url'}
将要在上执行时返回结果,其中:state=>2,kwargs=>{'url':'an url'}
从print_url函数返回:一个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
但是。。。这只是一个可怕的混乱,暴露了一堆内部细节,最好是留在引擎盖下


从根本上说,我不知道为什么这里需要一个装饰器,也就是说,使用这个装饰器有什么好处?

因为你正在重新装饰你已经装饰过的函数。这是一个非常快速的答案,确实解决了我的问题。谢谢decorator的用途是进行语法培训。我想习惯更复杂的编码方法。你的回答让我意识到我必须学习更多的装饰者,因为我真的不明白在for循环之前这个简单的任务是如何解决我的问题的@Dg_gt再次说明,一旦装饰
some_func=decorator(some_func)
,那么
some_func
指的是装饰器返回的任何内容。在本例中,您将返回一个包装器函数,并对其进行装饰。