defouter(a,b):definner(c,d):return c + dreturninner(a, b)outer(4, 7)# 11
Внутренние функции могут быть полезны при выполнении некоторых сложных задач более одного раза внутри другой функции. Это позволит избежать дублирования кода
Рассмотрим пример работы со строкой, когда внутренняя функция добавляет текст в свой аргумент:
Напишите функцию, которая принимает аргумент — число. В теле функции определите внутреннюю функцию, которая принимает строку и число. Значение строки по умолчанию — "Python". Число — это количество, сколько раз должна быть продублирована строка. Верните результат работы внутренней функции во внешней функции. Протестируйте программу.
Замыкания
Внутренняя функция может действовать как замыкание, если возвращает функцию
Замыкание — это функция, которая находится внутри другой функции и ссылается на переменные, объявленные в теле внешней функции
defouter_function(x):definner_function(y):return x + yreturn inner_functionclosure =outer_function(10)print(closure(5))# => 15# Замыкание (closure) позволяет сохранить значение между вызовами и может быть использовано внутри inner_function даже после того, как outer_function уже завершила свою работу
defadd_number(n):definner(x):return x + nreturn inneradd_five =add_number(5)# add_five хранит внутреннюю функцию c сохраненной переменной n == 5add_ten =add_number(10)# а add_ten сохранила n == 10print(add_five(3))# 8print(add_ten(3))# 13
Применение замыканий позволяет создавать функции с доступом к переменным, находящимся вне их области видимости и запоминать значения переменных
defpassword_protected(password):definner():if password =='secret':print("Access granted")else:print("Access denied")return innerlogin =password_protected('secret')login()# Access granted# таким образом функция login "запомнила" значение переменной password
Внутри внешней функции мы можем создавать переменные и изменять их во внутренних функциях
Для этого используется ключевое слово nonlocal
Пример использования замыкания и ключевого слова nonlocal для генерации ID:
Напишите функцию которая принимает имя в качестве аргумента и добавляет к имени заданную строку. Используйте замыкание.
Пример работы программы:
love_python =func("любит Python")love_python("Игорь")# Игорь любит Pythonlove_python("Татьяна")# Татьяна любит Python
Создайте функцию для генерации паролей. Функция должна принимать символы, из которых будет генерироваться пароль (установите для этого значение по умолчанию), и длину пароля. При каждом вызове она должна возвращать новый случайный пароль. Используйте замыкание и модули random и string
Пример работы программы:
generate_8_char_password =password_generator(8)print(generate_8_char_password())# Например: "rT3uF9pW"print(generate_8_char_password())# Еще один случайный пароль
Карринг
Карринг (currying) — это техника в функциональном программировании, при которой функция, принимающая несколько аргументов, преобразуется в последовательность функций, каждая из которых принимает по одному аргументу.
Это позволяет вам создавать более специализированные функции и частично применять аргументы.
# функция сложения двух чиселdefadd_nums(x,y):return x + y# применяем каррингdefadd(x):defadd_x(y):return x + yreturn add_x# Создаем функцию для сложения на 5add_5 =add(5)# Теперь можем использовать add_5 как отдельную функциюresult =add_5(3)# Результат: 8
Карринг удобен, когда вы хотите создавать множество функций, используя один и тот же "шаблон" и частично применять аргументы.
Практика:
Создайте функцию умножения двух чисел. Используйте карринг
Декораторы
Иногда нам нужно модифицировать существующую функцию, не меняя при этом исходный код
Декоратор — это функция, которая принимает одну функцию в качестве аргумента и возвращает другую функцию. По сути это "обертка" для нашей функции, которая добавляет ей функционал
deffunc_decorator(func):defwrapper(): print("---------что-то делаем перед вызовом функции ------") res =func()print("---------что-то делаем после вызова функции-------")return res return wrapperdefsome_func():print("Работает функция some_func!")func_decorator(some_func)()# ---------что-то делаем перед вызовом функции ------# Работает функция some_func!# ---------что-то делаем после вызова функции-------
Добавим аргументы и создадим дополнительную переменную для задекорированной функции:
deffunc_decorator(func):defwrapper(num1,num2): print("---------что-то делаем перед вызовом функции ------") res =func(num1, num2)print("---------что-то делаем после вызова функции-------")return res return wrapperdefsome_func(num1,num2):print(f"Работает функция some_func! Кстати числа равны {num1} и {num2}")upgrade_some_func =func_decorator(some_func)upgrade_some_func(5, 10)
Для того чтобы сделать декоратор универсальным, можно сразу указать все возможные аргументы (позиционные и ключевые) таким образом:
deffunc_decorator(func):defwrapper(*args,**kwargs): print("---------что-то делаем перед вызовом функции ------") res =func(*args, **kwargs)print("---------что-то делаем после вызова функции-------")return res return wrapperdefsome_func(num1,num2):print(f"Работает функция some_func! Кстати числа равны {num1} и {num2}")upgrade_some_func =func_decorator(some_func)upgrade_some_func(3, 5)
В Python есть "синтаксический сахар" — более удобная форма записи для декораторов:
@func_decorator# декорируем функцию some_funcdefsome_func(num1,num2):print(f"Работает функция some_func! Кстати числа равны {num1} и {num2}")some_func(3, 5)# т.к. мы декорировали функцию перед определением, она сработает вместе с декоратором
Пример — декоратор, измеряющий время работы функций:
import timedeftest_time(func):defwrapper(*args,**kwargs): start = time.time() res =func(*args, **kwargs) end = time.time() delta = end - startprint(f"Время работы: {delta} сек")return resreturn wrapper
Практика:
Напишите функцию конкатенации двух строк. Измерьте время её работы при помощи декоратора test_time
Определите декоратор test, который выводит строку start при вызове функции и строку end, когда функция завершает свою работу. Дополнительно: добавьте вывод имени запускаемой функции (используйте метод func.__name__)
Напишите декоратор, который возводит результат работы функции в квадрат. Примените его к любой функции, возвращающей число.
Напишите декоратор, который будет автоматически повторять выполнение функции, если она выбрасывает исключение.