ТЕОРИЯ И ПРАКТИКА

  • Ввод и вывод данных
    • Задачи
  • Условия
    • Задачи
  • Цикл for
    • Задачи
  • Строки
    • Задачи
  • Цикл while
    • Задачи
  • Списки
    • Задачи
  • Двумерные массивы
    • Задачи
  • Словари
    • Задачи
  • Множества
    • Задачи
  • Функции и рекурсия
    • Задачи

Занятие 10. Функции и рекурсии в питоне

Введение в код

Функция Python: назначение, примеры

Функция в Python — это именованный, независимый блок кода, который выполняет определённую задачу. Вы можете вызывать этот блок кода по имени в любой части вашей программы, когда вам нужно выполнить эту задачу. Вы уже использовали встроенные функции, например, print() для вывода информации на экран или len() для определения длины объекта.

# Пример использования встроенной функции print()
print("Привет, мир!")

# Пример использования встроенной функции len()
my_list = [1, 2, 3, 4, 5]
list_length = len(my_list)
print("Длина списка:", list_length)
                        

Зачем использовать функции

Основная причина — следование принципу DRY (Don't Repeat Yourself - не повторяйся). Если у вас есть участок кода, который нужно выполнить в нескольких местах программы, лучше вынести его в функцию. Это делает код более организованным, читаемым и простым в обслуживании. Вместо копирования и вставки одного и того же кода вы просто вызываете функцию.

Преимущества повторного использования кода

Повторное использование кода через функции даёт несколько ключевых преимуществ:

  • Надёжность: Вы отлаживаете код один раз в одном месте. Если вы найдёте ошибку, вам нужно будет исправить её только в теле функции, и все вызовы этой функции начнут работать правильно.
  • Экономия времени: Вам не нужно писать один и тот же код снова и снова.
  • Читаемость: Программа, разбитая на небольшие, логически завершённые функции, гораздо проще для понимания, чем один большой монолитный блок кода.

Функции: аргументы, определение в Python

Синтаксис функции def в Python

Для создания (или определения) собственной функции используется ключевое слово def. За ним следует имя функции, круглые скобки () и двоеточие :.

Синтаксис выглядит так: def имя_функции():

Тело функции и отступы

Весь код, который относится к функции, должен располагаться после двоеточия и иметь отступ (обычно 4 пробела). Отступ показывает Python, где начинается и где заканчивается тело функции.

Пример простой функции без параметров

Эта функция не принимает никакой информации извне. Она просто выполняет одно и то же действие каждый раз при вызове.

# Определение простой функции
def greet():
    """Эта функция просто печатает приветствие."""
    print("--------------------")
    print("Добро пожаловать!")
    print("--------------------")

# Вызов функции
print("Начало программы.")
greet()  # Вызываем функцию здесь
print("Программа продолжается.")
greet()  # И можем вызвать её снова
print("Конец программы.")
                        

Параметры и аргументы

Параметры в определении функции

Параметры — это переменные, которые вы указываете в скобках при определении функции. Они служат "заполнителями" для данных, которые функция будет получать при вызове.

Аргументы при вызове

Аргументы — это фактические значения, которые вы передаёте в функцию при её вызове. Эти значения присваиваются параметрам функции.

Передача разных типов данных

В функцию можно передавать данные любого типа: числа, строки, списки, словари и даже другие функции.

# Определение функции с одним параметром 'name'
def personal_greet(name):
    """Эта функция принимает имя в качестве аргумента и печатает персональное приветствие."""
    print(f"Привет, {name}! Как дела?")

# Определение функции для обработки разных типов данных
def process_data(data):
    """Эта функция выводит тип данных и их значение."""
    print(f"Получены данные: {data}")
    print(f"Тип данных: {type(data)}")
    print("---")

# Вызов функций с разными аргументами
personal_greet("Анна")  # "Анна" - это аргумент
personal_greet("Иван")  # "Иван" - это аргумент

process_data(100)                      # Передаём число
process_data("Это тестовая строка")    # Передаём строку
process_data([1, "два", 3.0])          # Передаём список
                        

Значение функции возвращаемое

Использование return

Часто функция должна не просто выполнить действие, а вычислить что-то и вернуть результат. Для этого используется оператор return. Когда Python встречает return, он немедленно выходит из функции и передаёт указанное значение в то место, где функция была вызвана.

Функции с возвратом и без

Функция без return неявно возвращает специальное значение None. Функции, которые что-то вычисляют, почти всегда используют return.

4.3. Сохранение результата в переменную

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

# Функция без возвращаемого значения (неявно вернёт None)
def print_sum(a, b):
    print(f"Сумма {a} и {b} равна: {a + b}")

# Функция с возвращаемым значением
def calculate_sum(a, b):
    """Эта функция вычисляет сумму двух чисел и возвращает её."""
    result = a + b
    return result

# Вызов функции без return
print_sum(5, 3)
result_from_print = print_sum(10, 2)
print(f"Результат, сохраненный из print_sum: {result_from_print}") # Выведет None

print("-" * 20)

# Вызов функции с return и сохранение результата
sum_result = calculate_sum(5, 3)
print(f"Результат, полученный из calculate_sum: {sum_result}")

# Можно использовать результат напрямую
if calculate_sum(10, 20) > 25:
    print("Сумма больше 25")
                        

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

Задание значений по умолчанию

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

Вызов функции с и без аргументов

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

# Параметр 'power' имеет значение по умолчанию 2
def raise_to_power(number, power=2):
    """Возводит число в указанную степень. По умолчанию в квадрат."""
    return number ** power

# Вызов функции без указания второго аргумента (используется значение по умолчанию)
result_squared = raise_to_power(5)
print(f"5 в степени 2 (по умолчанию) = {result_squared}")

# Вызов функции с указанием второго аргумента
result_cubed = raise_to_power(5, 3)
print(f"5 в степени 3 = {result_cubed}")
                        

Именованные аргументы, позиционные аргументы

Передача аргументов по имени

Помимо передачи аргументов по позиции (первый аргумент для первого параметра и т.д.), вы можете передавать их по имени (именованные или keyword-аргументы).

Повышение читаемости кода

Именованные аргументы делают код гораздо более читаемым, особенно если у функции много параметров. Вы точно видите, какое значение какому параметру присваивается. Порядок именованных аргументов не важен.

def create_user_profile(username, age, city, status="active"):
    """Создает профиль пользователя."""
    print(f"Создан профиль для {username}.")
    print(f"Возраст: {age}, Город: {city}, Статус: {status}")
    print("---")

# Позиционные аргументы (порядок важен)
create_user_profile("JohnDoe", 30, "New York")

# Именованные (keyword) аргументы (порядок не важен)
create_user_profile(city="Moscow", age=25, username="AnnaK")

# Смешанное использование: сначала позиционные, потом именованные
create_user_profile("PeterJ", 35, status="inactive", city="London")
                        

Переменное число параметров, обработка в Python

Аргументы *args в Python

Передача любого количества позиционных аргументов

Если вы хотите, чтобы функция могла принимать любое количество позиционных аргументов, используйте конструкцию *args (имя args — это соглашение, можно использовать любое другое, но со звёздочкой).

Обработка в виде кортежа

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

def sum_all_numbers(*args):
    """Суммирует все переданные числа."""
    print(f"Получен кортеж аргументов: {args}")
    total = 0
    for number in args:
        total += number
    return total

print(sum_all_numbers(1, 2, 3))
print(sum_all_numbers(10, 20, 30, 40, 50))
print(sum_all_numbers())
                        

Значения аргументов **kwargs в Python

Передача любого количества именованных аргументов

Для приёма любого количества именованных аргументов используется конструкция **kwargs (keyword arguments).

Обработка в виде словаря

Внутри функции kwargs становится словарём, где ключи — это имена аргументов, а значения — их значения.

def display_user_info(**kwargs):
    """Отображает информацию о пользователе, переданную в виде именованных аргументов."""
    print(f"Получен словарь аргументов: {kwargs}")
    for key, value in kwargs.items():
        print(f"{key.capitalize()}: {value}")
    print("---")

display_user_info(name="Игорь", age=28, city="Самара")
display_user_info(username="tech_guru", email="guru@example.com", has_premium=True)
                        

Совмещение *args и **kwargs

Порядок размещения в определении функции

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

Пример комбинированной функции

def process_data(processor_name, *data_points, **metadata):
    """Обрабатывает данные с мета-информацией."""
    print(f"Обработчик: {processor_name}")
    print(f"Точки данных (кортеж): {data_points}")
    print(f"Сумма точек данных: {sum(data_points)}")
    print("Метаданные (словарь):")
    for key, value in metadata.items():
        print(f"  - {key}: {value}")

process_data("MainProcessor", 10, 20, 30, source="Sensor A", timestamp="2025-05-06")
                        

Документация функции в Python, описание

Описание docstring в Python

Строка документации (docstring) — это строка, которая является первым выражением в теле функции. Она используется для описания того, что делает функция.

Размещение внутри функции

Docstring заключается в тройные кавычки ("""...""" или '''...''') и размещается сразу после строки def.

Получение описания через __doc__

Вы можете получить доступ к строке документации функции с помощью её специального атрибута __doc__.

def factorial(n):
    """
    Вычисляет факториал неотрицательного целого числа.

    Args:
        n (int): Неотрицательное целое число.

    Returns:
        int: Факториал числа n.
    """
    if n < 0:
        return "Факториал определен только для неотрицательных чисел"
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

# Вывод строки документации
print(factorial.__doc__)
                        

Рекурсивные алгоритмы в Python

Определение рекурсии в Python

Рекурсия — это процесс, при котором функция вызывает саму себя для решения задачи. Рекурсивный подход разбивает сложную задачу на более простые подзадачи того же типа.

Условие завершения

Ключевой элемент любой рекурсивной функции — это базовый случай (или условие завершения). Это условие, при котором функция перестаёт вызывать себя и возвращает конкретное значение. Без него рекурсия будет бесконечной.

Примеры: факториал, Фибоначчи в Python

Факториал числа [ n! ] определяется как произведение всех целых чисел от 1 до [ n ]. Рекурсивное определение: [ n! = n \times (n-1)! ], базовый случай [ 0! = 1 ].

# Рекурсивная функция для вычисления факториала
def factorial_recursive(n):
    """Рекурсивно вычисляет факториал."""
    # Базовый случай
    if n == 0 or n == 1:
        return 1
    # Рекурсивный шаг
    else:
        return n * factorial_recursive(n - 1)

# Рекурсивная функция для чисел Фибоначчи
def fibonacci_recursive(n):
    """Рекурсивно вычисляет n-ое число Фибоначчи."""
    # Базовые случаи
    if n <= 1:
        return n
    # Рекурсивный шаг
    else:
        return fibonacci_recursive(n - 2) + fibonacci_recursive(n - 1)

print(f"Факториал 5: {factorial_recursive(5)}")
print(f"10-ое число Фибоначчи: {fibonacci_recursive(10)}") # Осторожно, для больших n очень медленно!
                        

Ограничения рекурсии

Глубина рекурсии

В Python есть ограничение на максимальное количество рекурсивных вызовов (глубину рекурсии), чтобы предотвратить переполнение стека вызовов.

Ошибка RecursionError

Если вы превысите этот лимит, Python сгенерирует ошибку RecursionError.

Изменение лимита через sys

Вы можете проверить и изменить этот лимит с помощью модуля sys. Делать это следует с осторожностью.

import sys

# Получить текущий лимит рекурсии
current_limit = sys.getrecursionlimit()
print(f"Текущий лимит глубины рекурсии: {current_limit}")

# Установить новый лимит
# sys.setrecursionlimit(2000)
# print(f"Новый лимит глубины рекурсии: {sys.getrecursionlimit()}")

# Пример, вызывающий RecursionError (если лимит стандартный, около 1000)
try:
    factorial_recursive(1500)
except RecursionError as e:
    print(f"Произошла ошибка: {e}")
                        

Альтернативы рекурсии

Для многих задач, решаемых рекурсивно, существуют более эффективные итеративные (с использованием циклов) решения.

Итеративный подход к факториалу

Использование цикла for или while часто работает быстрее и не имеет ограничений по глубине рекурсии.

def factorial_iterative(n):
    """Итеративно вычисляет факториал."""
    if n < 0: return "Ошибка"
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

print(f"Итеративный факториал 5: {factorial_iterative(5)}")
print(f"Итеративный факториал 1500 (без ошибки): {factorial_iterative(1500)}")
                        

Итеративная реализация Фибоначчи

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

def fibonacci_iterative(n):
    """Итеративно вычисляет n-ое число Фибоначчи."""
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

print(f"Итеративное 10-ое число Фибоначчи: {fibonacci_iterative(10)}")
print(f"Итеративное 50-ое число Фибоначчи: {fibonacci_iterative(50)}")
                        

Lambda-функции в Python: применение

Синтаксис lambda

Lambda-функции — это небольшие анонимные (без имени) функции. Они определяются с помощью ключевого слова lambda. Синтаксис: lambda аргументы: выражение. Они могут иметь любое количество аргументов, но только одно выражение, результат которого они возвращают.

Примеры Python

Lambda-функции удобны, когда вам нужна простая функция на короткое время.

# Обычная функция
def add(x, y):
    return x + y

# Эквивалентная lambda-функция
add_lambda = lambda x, y: x + y

print(f"Обычная функция: {add(10, 5)}")
print(f"Lambda-функция: {add_lambda(10, 5)}")
                        

Использование в map, filter, sorted

Основное применение лямбда-функций — в качестве аргументов для функций высшего порядка, таких как map, filter и sorted.

numbers = [1, 2, 3, 4, 5, 6, 7, 8]

# map: применяет функцию к каждому элементу
squared_numbers = list(map(lambda x: x**2, numbers))
print(f"Квадраты чисел (map): {squared_numbers}")

# filter: отбирает элементы, для которых функция возвращает True
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Четные числа (filter): {even_numbers}")

# sorted: использует lambda в качестве ключа для сортировки
points = [(1, 5), (3, 2), (8, -1), (4, 4)]
# Сортировка по второму элементу кортежа
points_sorted = sorted(points, key=lambda point: point[1])
print(f"Сортировка по Y (sorted): {points_sorted}")
                        

Функции как аргументы

Передача функций в другие функции

В Python функции являются "объектами первого класса". Это означает, что их можно присваивать переменным, хранить в структурах данных и, что самое важное, передавать в качестве аргументов другим функциям.

Примеры: map, filter, custom apply()

Функции, которые принимают другие функции в качестве аргументов, называются функциями высшего порядка. map и filter — отличные встроенные примеры. Мы также можем написать свою.

def square(x):
    """Возводит число в квадрат."""
    return x * x

def is_odd(x):
    """Проверяет, является ли число нечетным."""
    return x % 2 != 0

def apply_function_to_list(func, data_list):
    """
    Применяет переданную функцию 'func' к каждому элементу списка 'data_list'.
    """
    result = []
    for item in data_list:
        result.append(func(item))
    return result

my_numbers = [1, 2, 3, 4, 5]

# Передаем функцию square в нашу кастомную функцию
squared_by_custom = apply_function_to_list(square, my_numbers)
print(f"Применили 'square' через custom apply: {squared_by_custom}")

# Используем filter с заранее определенной функцией is_odd
odd_numbers = list(filter(is_odd, my_numbers))
print(f"Отфильтровали с 'is_odd': {odd_numbers}")