Найти в Дзене

Функции, декораторы и замыкания в Python

Python предлагает мощные инструменты для работы с функциями, декораторами и замыканиями. В этой статье мы разберем их синтаксис, применение и внутреннее устройство. Функции определяются через ключевое слово `def`: def greet(name: str) -> str: """Возвращает приветствие.""" return f"Hello, {name}!" - Параметры: позиционные (`name`), именованные (`name="User"`), `*args` (список аргументов), `**kwargs` (словарь ключевых аргументов). - Типы: аннотации типов (`str`) и `->` для возвращаемого значения. Анонимные функции задаются через `lambda`: square = lambda x: x ** 2 # Эквивалентно def square(x): return x ** 2 print(square(3)) # 9 Ограничение: Лямбды могут содержать только одно выражение. - Локальные (local): Переменные внутри функции. - Глобальные (global): Переменные уровня модуля. - Нелокальные (nonlocal): Переменные из внешней функции (для вложенных функций). - Enclosing: Область видимости внешней функции. Пример: x = "global" 7def outer(): ....x = "outer" .....def inner(): ........n
Оглавление

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"
7def 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__`:

```python

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, позволяющие писать гибкий и выразительный код. Понимание их работы и внутренней механики открывает путь к созданию мощных абстракций и паттернов.