Замыкания¶
Функция, которая вложена в другую функцию и использующая переменные из области видимости родительской функции.
Как применять:
def foo(a):
def bar(b):
return a*b
return bar
mult_10 = foo(10)
mult_10(2)
# 20
mult_10(3)
# 30
# Конвеер функций
foo(5)(2)
# 10
Если мы хотим изменять значение какой-либо переменной из родительской функции, то нужно объявить эту переменную как nonlocal
def foo(a):
def bar(b)
nonlocal a
a += b
return a
return bar
baz = foo(10)
baz(2)
# 12
baz(3)
# 15
Декоратор¶
Частный случай замыкания
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Декоратор. Аргумент функции {a=}")
return func(*args, **kwargs)
return wrapper
@decorator # Синтаксический сахар для foo = decorator(foo)
def foo(a):
print(f"Исходная функция с аргументом {a=}")
foo(10)
# Декоратор. Аргумент функции 10
# Исходная функция с аргументом 10
Декоратора wraps внутри доекартора используется для переноса всех атрибутов исходной функции на ту, которая возваращается из декоратора. Нужно использовать всегда для полноценной работы задекорированной функции.
Декоратор с аргументами¶
Для этого нужно использовать фабрику декораторов, т.е. функцию, которая создает декоратор:
from functools import wraps
def decor_fabric(some_var, some_arg=None):
def decor(func):
setattr(func, "value", some_var)
setattr(func, "some_arg", some_arg)
@wraps(func)
def function(*args, **kwargs):
# Здесь тоже можно использовать some_var и some_arg
print(some_var, some_arg)
return func(*args, **kwargs)
return function
return decor
@decor_fabric(10, some_arg="Test")
def foo():
pass
foo.value
# 10
foo.some_arg
# Test
foo()
# 10 Test
По-другому это можно записать так
decorator = decor_fabric(10, some_arg)
foo = decorator(foo)
Смысл в том, что decor_fabirc вызывается как функция с парамтерами, вовзращая доекоратор, который уже применяется к функции.