eval(source) выполняет строку-выражение, переданную ей в качестве обязательного аргумента, и возвращает результат выполнения этой строки.
exec(source) принимает блок кода и выполняет его, возвращая значение None.
Замыкания
Замыкание — функция, которая “помнит” значения переменных из внешней области видимости, даже после того как эта область завершила выполнение. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.
воздержания от использования глобальных переменных
воздержания от создания ненужных типов данных (классов)
реализации декораторов
Инфо
Фабричная функция — функция, которая создает и возвращает другую функцию (или объект), в том числе замыкания.
Пример 1
В этом примере функция greeting_creator() служит фабрикой для создания и настройки функции приветствия. Вложенная функция greet() может обращаться к аргументу greeting_word своей родительской функции greeting_creator(), поскольку является замыканием:
Нелокальные переменные — переменные, которые находятся внешне по отношению к текущей функции, но не являются глобальными.
И-так, вложенная функция видит переменные в родительской функции, которые называются нелокальными или свободными.
Вложенная функция может видеть переменные внешней, но изменять их — нет. Чтобы суметь так сделать, переменную во внутренней функции нужно явно обозначить ключевым словом nonlocal:
P.S. без nonlocal получили бы ошибку UnboundLocalError.
Декоратор
Декоратор — функция, которая принимает в качестве аргумента другую функцию, добавляет в неё дополнительный функционал и возвращает функцию с изменённым поведением.
Декораторы обычно реализуются как вложенные функции, использующие замыкание, чтобы сохранить состояние и изменить поведение оборачиваемой функции.
Здесь sample_decorator() является функцией-декоратором. Как можно заметить, она является функцией высшего порядка. Внутри sample_decorator() определена другая функция — обёртка, которая обёртывает передаваемую функцию say() и затем изменяет её поведение. Декоратор возвращает эту обёртку.
Само декорирование происходит в строке say = sample_decorator(say), после которого переменная say указывает на функцию wrapper().
Синтаксический сахар
Python позволяет использовать декораторы более простым способом с помощью символа @.
Если бы нужно было обойтись без синтаксического сахара: greet = bold(italic(say))
Декорирование параметрической функции
Чтобы внутренняя функция декоратора могла обрабатывать аргументы декорируемой функции, нужно использовать *args и **kwargs в качестве параметров внутренней функции:
# Вывод:
<b>Hello Ivan!</b>
<b>Hello world!</b>
<b>Hello Ivan Ivanov!</b>
В примере выше вложенная функция wrapper() принимает произвольное число позиционных и именованных аргументов и передает их в декорируемую функцию func(). Теперь декоратор @bold будет работать как для функций, которые вообще не принимают аргументы, так и для функций которые принимают произвольное количество позиционных и именованных аргументов.
Чтобы сохранить имя функции и её строку документации при кодировании, нужно передать атрибуты декорируемой функции в обёртку. Можно сделать это вручную: wrapper.__name__ = func.__name__ и wrapper.__doc__ = func.__doc__, но лучше использовать декоратор @functools.wraps(func) для функции-обёртки
Шаблоны
Общий шаблон
Все декораторы делают примерно одно и то же, поэтому существует шаблон декораторов:
1
2
3
4
5
6
7
8
9
10
11
importfunctoolsdefdecorator(func):@functools.wraps(func)defwrapper(*args,**kwargs):# Что-то выполняется до вызова декорируемой функцииvalue=func(*args,**kwargs)# Декорируется возвращаемое значение функции# или что-то выполняется после вызова декорируемой функцииreturnvaluereturnwrapper
Измерение времени работы
Следующий декоратор измеряет и выводит время выполнения декорируемой функции. Он вычисляет время непосредственно перед запуском функции и сразу после ее завершения и выводит разницу:
importfunctools,timedeftimer(func):@functools.wraps(func)defwrapper(*args,**kwargs):start=time.perf_counter()val=func(*args,**kwargs)end=time.perf_counter()work_time=end-startprint(f'Execution time {func.__name__}: {round(work_time,4)} sec.')returnvalreturnwrapper@timerdeftest(n):returnsum([(i/99)**2foriinrange(n)])@timerdefsleep(n):time.sleep(n)res1=test(10000)res2=sleep(4)print(f'test func result = {res1}')print(f'sleep func result = {res2}')
Для того чтобы создать декоратор, принимающий аргументы, необходимо добавить еще один уровень вложенности, то есть создать функцию, которая возвращает нужный декоратор. Например:
importfunctools,timedeftimer(iters=1):defdecorator(func):@functools.wraps(func)defwrapper(*args,**kwargs):total=0foriinrange(iters):start=time.perf_counter()value=func(*args,**kwargs)end=time.perf_counter()total+=end-startprint(f'Average execution time {func.__name__}: {round(total/iters,4)} sec.')returnvaluereturnwrapperreturndecorator@timerdeftest(n):returnsum([(i/99)**2foriinrange(n)])@timerdefsleep(n):time.sleep(n)res1=test(10000)res2=sleep(4)print(f'test func result = {res1}')print(f'sleep func result = {res2}')