0Pricing
Coding Interview Prep · Урок

Функции, замыкания и лямбда-выражения

Определяйте повторно используемые вспомогательные функции, применяйте аргументы по умолчанию и используйте лямбда-выражения для сортировки и функциональных приёмов в задачах на собеседованиях

«Функции, замыкания и лямбда-выражения» — бесплатный урок Coding Interview Prep на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Coding Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Coding Interview Prep содержит 4 уроков всего.

Определение функций в Python

Функции в Python являются объектами первого класса: их можно передавать, возвращать и сохранять. Хорошо названные вспомогательные функции делают код для собеседования понятнее. Сначала обрабатывайте пограничные случаи.

def is_palindrome(s: str) -> bool:
    '''Return True if s reads the same forwards and backwards.'''
    s = s.lower()
    return s == s[::-1]

print(is_palindrome('Racecar'))  # True
print(is_palindrome('hello'))    # False

# Functions as values
checks = [is_palindrome]
print(checks[0]('level'))  # True

Аргументы по умолчанию и именованные аргументы

Значения по умолчанию позволяют не указывать аргументы при вызове. Но никогда не используйте изменяемое значение по умолчанию, например список, — все вызовы будут совместно его использовать. Исправление: задайте None по умолчанию и создайте список внутри функции. См. код.

# WRONG: shared mutable default
def bad_append(val, lst=[]):
    lst.append(val)
    return lst

print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2]  surprise!

# CORRECT: use None sentinel
def good_append(val, lst=None):
    if lst is None:
        lst = []
    lst.append(val)
    return lst

print(good_append(1))  # [1]
print(good_append(2))  # [2]

*args и **kwargs

*args собирает дополнительные позиционные аргументы в кортеж, а **kwargs — дополнительные именованные аргументы в словарь. Символ * также распаковывает последовательность при вызове функции.

def total(*args):
    return sum(args)

print(total(1, 2, 3))     # 6
print(total(1, 2, 3, 4))  # 10

# Unpack a list as positional args
point = [3, 7]
print(max(*point))   # 7

# **kwargs
def greet(**kwargs):
    name = kwargs.get('name', 'World')
    return f'Hello, {name}!'

print(greet(name='Alice'))  # Hello, Alice!

Лямбда-выражения

lambda — это небольшая функция в одну строку: lambda params: expression. Она идеально подходит как быстрый ключ для sorted или max. Для чего-то более сложного именованное определение def читается понятнее.

# Lambda as sort key
pairs = [(1, 3), (2, 1), (3, 2)]
sorted_by_second = sorted(pairs, key=lambda p: p[1])
print(sorted_by_second)  # [(2,1),(3,2),(1,3)]

# Lambda with map
double = list(map(lambda n: n * 2, [1, 2, 3]))
print(double)  # [2, 4, 6]

# Named function is clearer for complex logic
def sort_key(p):
    return (p[1], -p[0])  # secondary sort
print(sorted(pairs, key=sort_key))

Вложенные функции и замыкания

Замыкание — это функция, которая запоминает переменные из окружающей её функции. Определите вспомогательную функцию внутри другой функции, и она сможет использовать внешние переменные — это удобно для DFS.

def make_counter(start=0):
    count = [start]  # list to allow mutation
    def increment():
        count[0] += 1
        return count[0]
    return increment

counter = make_counter()
print(counter())  # 1
print(counter())  # 2

# Alternative: nonlocal keyword
def make_counter2():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment

Замыкания в рекурсивном DFS

Распространённый шаблон: определите вспомогательную функцию dfs внутри основной функции и позвольте ей собирать результаты во внешней переменной. Внутренняя функция замыкает эту область видимости.

def max_depth(root):
    '''Closure pattern for tree DFS.'''
    max_d = [0]  # mutable container for closure

    def dfs(node, depth):
        if node is None:
            return
        max_d[0] = max(max_d[0], depth)
        dfs(node.left, depth + 1)
        dfs(node.right, depth + 1)

    dfs(root, 1)
    return max_d[0]
# This pattern avoids a class or global state.

functools.lru_cache для мемоизации

lru_cache (или @cache в версии 3.9 и новее) запоминает результаты функции по её аргументам, превращая наивное вычисление чисел Фибоначчи из O(2^n) в O(n). Аргументы должны быть хешируемыми, поэтому используйте кортежи.

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(40))   # 102334155  fast!
print(fib.cache_info())  # hits, misses, size

Функции высшего порядка

Функция высшего порядка принимает функцию или возвращает её. sorted, map и filter относятся к таким функциям. Создание собственных функций позволяет абстрагировать повторяющиеся шаблоны. В коде показана композиция.

def apply_twice(f, x):
    return f(f(x))

print(apply_twice(lambda n: n * 2, 3))  # 12  (3*2*2)

# Composing transformations
def compose(f, g):
    return lambda x: f(g(x))

double = lambda n: n * 2
add_one = lambda n: n + 1
double_then_add = compose(add_one, double)
print(double_then_add(5))  # 11  ((5*2)+1)

Рекурсия со вспомогательными функциями

Во многих решениях внешняя функция выполняет подготовку и обрабатывает пограничные случаи, а внутренняя вспомогательная функция выполняет рекурсию. Чистый внешний интерфейс, изолированная логика. См. код.

def flatten(nested):
    '''Flatten an arbitrarily nested list.'''
    result = []

    def _flatten(lst):
        for item in lst:
            if isinstance(item, list):
                _flatten(item)
            else:
                result.append(item)

    _flatten(nested)
    return result

print(flatten([1, [2, [3, 4], 5], 6]))
# [1, 2, 3, 4, 5, 6]

Частичные функции с functools.partial

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

from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube   = partial(power, exp=3)

print(square(5))   # 25
print(cube(3))     # 27
print(list(map(square, [1, 2, 3, 4])))  # [1, 4, 9, 16]

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

В Python 3 отказались от функций сравнения с двумя аргументами, но cmp_to_key возвращает их для сложных порядков сортировки — например, чтобы расположить числа так, чтобы их конкатенация была наибольшей. См. код.

from functools import cmp_to_key

def largest_number(nums):
    def compare(a, b):
        if a + b > b + a: return -1
        if a + b < b + a: return 1
        return 0
    strs = list(map(str, nums))
    strs.sort(key=cmp_to_key(compare))
    result = ''.join(strs)
    return '0' if result[0] == '0' else result

print(largest_number([3, 30, 34, 5, 9]))  # '9534330'

Быстрая проверка

Быстрая проверка — покажите, что Вы узнали о функциях, замыканиях и лямбдах. Сделайте вдох — и приступайте. 🚀

Итоги урока

Итоги: никогда не используйте изменяемые значения по умолчанию (используйте None), замыкания позволяют внутренним вспомогательным функциям обращаться к внешним переменным для DFS, а инструменты functools, такие как lru_cache, помогают создавать быстрые решения.

Часто задаваемые вопросы

Урок «Функции, замыкания и лямбда-выражения» бесплатный?

Да — полный текст урока «Функции, замыкания и лямбда-выражения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Coding Interview Prep, подпишись на CoddyKit PRO. Курс Coding Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «Функции, замыкания и лямбда-выражения»?

Определяйте повторно используемые вспомогательные функции, применяйте аргументы по умолчанию и используйте лямбда-выражения для сортировки и функциональных приёмов в задачах на собеседованиях Ты практикуешь Coding Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Coding Interview Prep?

Предыдущий опыт не требуется. Coding Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Функции, замыкания и лямбда-выражения»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Coding Interview Prep?

Да. Каждый урок Coding Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Списки, кортежи и срезы
  2. Словари и множества в Python
  3. Генераторы коллекций и встроенные функции
  4. Функции, замыкания и лямбда-выражения
← Назад к Coding Interview Prep