Как в декораторе в Python определить счетчик другой функции?
Реализую нахождение корней уравнения с одной переменной методом дихотомии. Там суть: отрезок несколько раз делится пополам. Если функция на середине отрезка составляет ноль либо меньше заданной погрешности - корень найден. Иначе деление продолжаем, и за границы отрезка принимается то левый, то правый его "рубежи".
Вывод результатов организовал в функции-обертке, чтобы данный функционал вывода не влиял на расчет времени исполнения главной задачи - поиска корней.
Требуется, кроме прочего, указать количество итераций (сколько раз делили отрезок пополам). Как сделал сейчас (и понимаю сам, что неверно), не получается. Ошибка:
NameError: name 'counter' is not defined
Как быть? Код:
import numpy as np
def print_dichotomy(dichotomy):
def wrapper(a,b,n, eps):
print('Корни по методу дихотомии находятся в точках:')
print(', '.join(map(lambda x: f'{x:.4f}', res)))
print('Количество итераций: ', counter) # ЗДЕСЬ ОШИБКА
return wrapper
def f(x):
return 1.2-np.log(x)-4*np.cos(2*x)
@print_dichotomy
def dichotomy (a,b,n, eps): # отрезок от a до b делим на n частей, погрешность eps
"""
Функция отделения и уточнения корня
"""
assert a!=0, 'a равно 0'
assert b!=0, 'b равно 0'
# сначала отделим корни
grid=np.linspace(a, b, n)
# далее уточним корни
counter=0
for x,y in zip(grid, grid[1:]):
if f(x) * f(y) > 0: # если на отрезке нет корня, смотрим следующий
continue
root = None
while ( abs(f(y)-f(x)) )>eps: # пока отрезок больше заданной погрешности, выполняем нижестоящие операции:
mid = (y+x)/2 # получаем середину отрезка
if f(mid) == 0 or f(mid)<eps: # если функция в середине отрезка равну нулю или меньше погрешности:
root = mid # корень равен серединному значению
counter+=1
break
elif (f(mid) * f(x)) < 0: # иначе если произведение функции в середине отрезка на функцию в т. а <0
y = mid # серединой становится точка b
else:
x = mid #в другом случае - точка а
if root:
yield root
Ответы (1 шт):
Вы получаете сообщение об ошибке, потому что counter является локальной переменной функции dichotomy. Она существует только внутри области видимости этой функции. Вне этой функции, данная переменная не видна.
И поскольку функция dichotomy не возвращает в результате своей работы наружу значение переменной counter никаким образом, то по завершении работы функции, переменная counter перестает существовать.
Следовательно, нужно переопределить counter таким образом, чтобы она находилась не в локальной, а в глобальной области видимости. Для этого можно использовать инструкцию global внутри функции dichotomy. Это не самое красивое решение, но оно позволит решить поставленную вами задачу. Таким образом, counter становится глобальной переменной, и мы инициализируем ее до вызова функции dichotomy. Теперь мы сможем использовать значение переменной counter после выполнения функции dichotomy.
Второй момент, на который хотел бы обратить внимание - я не увидел, чтобы внутри функции-обертки wrapper вы вызывали передаваемую в нее функцию. По этой причине возникает еще одна ошибка, что переменная res не определена. Вместо нее нужно как раз поместить вызов исходной функции, полученной в параметрах функции print_dichotomy.
В результате сказанного, код может выглядеть следующим образом:
import numpy as np
def print_dichotomy(func):
def wrapper(*args, **kwargs):
print('Корни по методу дихотомии находятся в точках:')
print(', '.join(map(lambda x: f'{x:.4f}', func(*args, **kwargs))))
print('Количество итераций: ', counter)
return wrapper
def f(x):
return 1.2 - np.log(x) - 4 * np.cos(2 * x)
@print_dichotomy
def dichotomy(a, b, n, eps): # отрезок от a до b делим на n частей, погрешность eps
"""
Функция отделения и уточнения корня
"""
global counter
assert a != 0, 'a равно 0'
assert b != 0, 'b равно 0'
# сначала отделим корни
grid = np.linspace(a, b, n)
# далее уточним корни
for x, y in zip(grid, grid[1:]):
if f(x) * f(y) > 0: # если на отрезке нет корня, смотрим следующий
continue
root = None
while (abs(f(y) - f(x))) > eps: # пока отрезок больше заданной погрешности, выполняем нижестоящие операции:
mid = (y + x) / 2 # получаем середину отрезка
if f(mid) == 0 or f(mid) < eps: # если функция в середине отрезка равну нулю или меньше погрешности:
root = mid # корень равен серединному значению
counter += 1
break
elif (f(mid) * f(x)) < 0: # иначе если произведение функции в середине отрезка на функцию в т. а <0
y = mid # серединой становится точка b
else:
x = mid # в другом случае - точка а
if root:
yield root
counter = 0
dichotomy(1, 11, 15, 0.01)