Перейти к содержанию

Замыкания

Функция, которая вложена в другую функцию и использующая переменные из области видимости родительской функции.

Как применять:

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 вызывается как функция с парамтерами, вовзращая доекоратор, который уже применяется к функции.