Предлагаемый повторяющийся вопрос, Область действия переменных в декораторах Python - изменение параметров дает полезную информацию, объясняющую, почему wrapper_repeat
считает nbrTimes
локальной переменной и как можно использовать nonlocal
чтобы он распознал nbrTimes
, определенный repeat
. Это исправит исключение, но я не думаю, что это полное решение в вашем случае. Ваша декорированная функция все равно не повторится.
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
nonlocal nbrTimes
while nbrTimes != 0:
nbrTimes -= 1
return func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")
Результат:
displaying: foo
displaying: bar
«foo» и «bar» отображаются только один раз, а «baz» отображается ноль раз. Я предполагаю, что это не желаемое поведение.
Первые два вызова display
не повторяются из-за return func(*args, **kwargs)
внутри вашего цикла while
. Оператор return приводит к немедленному завершению wrapper_repeat
, и дальнейшие итерации while
не выполняются. Так что никакая декорированная функция не повторится более одного раза. Одним из возможных решений является удаление return
и просто вызов функции.
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
nonlocal nbrTimes
while nbrTimes != 0:
nbrTimes -= 1
func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")
Результат:
displaying: foo
displaying: foo
«foo» отображается дважды, но теперь не отображаются ни «bar», ни «baz». Это потому, что nbrTimes
используется всеми экземплярами вашего декоратора, благодаря nonlocal
. как только display("foo")
уменьшает nbrTimes
до нуля, он остается равным нулю даже после завершения вызова. display("bar")
и display("baz")
выполнят свои декораторы, увидят, что nbrTimes
равен нулю, и завершат работу, не вызывая декорированную функцию вообще.
Так что получается, что вы не хотите, чтобы ваш счетчик циклов был нелокальным. Но это означает, что вы не можете использовать nbrTimes
для этой цели. Попробуйте создать локальную переменную на основе значения nbrTimes
'и уменьшите ее вместо этого.
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
times = nbrTimes
while times != 0:
times -= 1
func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")
Результат:
displaying: foo
displaying: foo
displaying: bar
displaying: bar
displaying: baz
displaying: baz
... И пока вы занимаетесь этим, вы можете использовать цикл for
вместо while
.
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(nbrTimes):
func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")