Синтаксис функций и лямбда-выражений

Синтаксис функций и лямбда-выражений


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


Синтаксис функций и лямбда-выражений

1. Объявление функций

Функции определяются через ключевое слово def:

def greet(name: str) -> str:
    """Возвращает приветствие."""
    return f"Hello, {name}!"
  • Параметры: позиционные (name), именованные (name="User"), *args (список аргументов), **kwargs (словарь ключевых аргументов).
  • Типы: аннотации типов (str) и -> для возвращаемого значения.

2. Лямбда-функции

Анонимные функции задаются через lambda:

square = lambda x: x ** 2  # Эквивалентно def square(x): return x ** 2
print(square(3))  # 9

Ограничение: Лямбды могут содержать только одно выражение.


Области видимости переменных

  • Локальные (local): Переменные внутри функции.
  • Глобальные (global): Переменные уровня модуля.
  • Нелокальные (nonlocal): Переменные из внешней функции (для вложенных функций).
  • Enclosing: Область видимости внешней функции.

Пример:

x = "global"

def outer():
    x = "outer"
    def inner():
        nonlocal x  # Ссылается на x из outer()
        x = "inner"
    inner()
    print(x)  # "inner"

outer()
print(x)  # "global"

Лямбды и функции высшего порядка

1. map, filter, zip

  • map: Применяет функцию к каждому элементу:
    numbers = [1, 2, 3]
    squared = list(map(lambda x: x ** 2, numbers))  # [1, 4, 9]
  • filter: Фильтрует элементы:
    even = list(filter(lambda x: x % 2 == 0, numbers))  # [2]
  • zip: Объединяет элементы итерируемых объектов:
    names = ["Alice", "Bob"]
    ages = [25, 30]
    combined = list(zip(names, ages))  # [("Alice", 25), ("Bob", 30)]

2. Функции как объекты первого класса

Функции можно:

  • Присваивать переменным: func = greet.
  • Передавать как аргументы: def apply(func, arg): return func(arg).
  • Возвращать из других функций:
    def multiplier(n):
        return lambda x: x * n
    double = multiplier(2)

Декораторы

1. Простой декоратор (без параметров)

Декоратор — функция, которая принимает другую функцию и возвращает новую:

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logger
def calculate(a, b):
    return a + b

calculate(3, 4)  # Выводит: "Calling calculate", возвращает 7

2. Параметризованные декораторы

Требуют дополнительного уровня вложенности:

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello")

say_hello()  # Выводит "Hello" три раза

3. Время выполнения декораторов

Декораторы выполняются сразу после определения функции (при загрузке модуля).

4. Декораторы через классы

Используйте метод __call__:

class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        print(f"Time: {time.time() - start} sec")
        return result

@Timer
def heavy_task():
    time.sleep(2)

heavy_task()  # Замеряет время выполнения

Замыкания (Closures)

1. Пример использования

Замыкание запоминает переменные из внешней области видимости:

def counter():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment

c = counter()
print(c(), c(), c())  # 1, 2, 3

2. Внутреннее устройство

  • nonlocal: Позволяет изменять переменные из внешней функции.
  • __closure__: Содержит ячейки с захваченными переменными.
  • co_freevars: Имена захваченных переменных.
  • cell_contents: Значение в ячейке.
def outer():
    x = 10
    def inner():
        print(x)
    return inner

func = outer()
print(func.__closure__[0].cell_contents)  # 10

Метаинформация функций

  • Документация: __doc__ для доступа к docstring.
  • Аннотации типов: __annotations__ хранит типы аргументов.
    def add(a: int, b: int) -> int:
        """Складывает два числа."""
        return a + b
    
    print(add.__doc__)  # "Складывает два числа."
    print(add.__annotations__)  # {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

Рекомендации

  1. Декораторы: Используйте functools.wraps для сохранения метаданных функции.
  2. Замыкания: Избегайте цикловых зависимостей в захваченных переменных.
  3. Типизация: Аннотации упрощают чтение кода и проверку типов (например, через mypy).

Заключение
Функции, декораторы и замыкания — ключевые элементы Python, позволяющие писать гибкий и выразительный код. Понимание их работы и внутренней механики открывает путь к созданию мощных абстракций и паттернов.