Задание на изучение функций в Python
- В качестве практической работы необходимо нарисовать блок-схемы работы алгоритма решения задач и загрузить в свой репозиторий в течение дня.
- В качестве домашней работы необходимо реализовать программы по ранее созданным алгоритмам решения задач на языке Python и загрузить в свой репозиторий до крайнего срока.
Даты сдачи оговариваются в канале группы.
Пользователь вводит строку. Нужно проверить, правильно ли в ней расставлены скобки 3 видов (), {}, []. Примеры:
- пв(ап) - правильно
- (вав(вып)вввв)ввв - правильно
- (апвыпыв - неправильно
- (ывп)) - неправильно
- авы) выавы( - неправильно (!)
- выпыв[()] - правильно
- [(выаыывпыв]) - неправильно
Вывести запрошенный элемент ряда Фибоначчи, полученный с помощью рекурсивной функции.
С помощью регулярных выражений найдите все аббревиатуры в строке. Аббревиатурой считается слово состоящее из заглавных букв длиной не более 6 символов, не разделённое небуквенным разделителем.
Создайте генератор-функцию factorials(n), которая поочерёдно возвращает факториалы чисел от 1 до n.
Функция — именованный блок кода, который может принимать аргументы, выполнять действия и возвращать значение. Функции используются для структурирования кода, повторного использования и сокращения дублирования. Если функция ничего не возвращает, то это уже процедура (Но в действительности, в Python, если нет return, то возвращается None).
- Наименование отображает то, что делает функция.
- Одна функция - одно назначение.
- Функция должна быть универсальной.
- Результат работы функции должен быть стабильным при различных входных данных.
def <имя_функции>(<параметры>):
""" Докстринг — описание функции."""
<тело>
return <значение> # опционально
Аргументы по порядку:
def add(a, b):
return a + b
add(2, 3) # 5
Можно передать по имени:
add(b=3, a=2)
def power(x, n=2):
return x ** n
power(3) # 9
power(3, 3) # 27
Не используйте изменяемые объекты как значения по умолчанию (list/dict), если вы не понимаете поведение — они будут общими для всех вызовов.
def bad_append(x, lst=[]):
lst.append(x)
return lst
bad_append(1) # [1]
bad_append(2) # [1, 2] <- часто неожиданно
Правильный вариант:
def safe_append(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
def sum_all(*args):
total = 0
for v in args:
total += v
return total
sum_all(1,2,3) # 6
Внутри args — кортеж.
def print_info(**kwargs):
for k, v in kwargs.items():
print(k, v)
print_info(name="Anna", age=30)
Внутри kwargs — словарь.
def func(a, b, /, c, *, d):
""" # a, b — позиционные только
# c — либо позиционная, либо именованная
# d — именованный только."""
return a + b + c + d
Слэши / и * полезны для контроля API функции.
Описание функции для пояснения и автодокументации:
def add(a, b):
""" Возвращает сумму a и b."""
return a + b
Доступ: help(add) или add.doc.
LEGB — Local, Enclosing, Global, Builtins.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
Чтобы изменить внешнюю переменную в enclosing scope (не глобальную), используйте nonlocal; для глобальной — global.
def counter():
cnt = 0
def inc():
nonlocal cnt
cnt += 1
return cnt
return inc
c = counter()
c() # 1
c() # 2
Когда вложенная функция захватывает переменные из внешней области:
def make_power(n):
def power(x):
return x ** n
return power
square = make_power(2)
square(5) # 25
Это полезно для фабрик функций и функционального стиля.
Короткие функции в одну строку:
f = lambda x, y: x + y
f(2, 3) # 5
pairs = [(1, 'b'), (2, 'a')]
pairs.sort(key=lambda p: p[1])
Лямбды хороши для простых однострочных выражений; не злоупотребляйте — для сложного кода лучше def.
Декоратор — это функция, которая принимает функцию и возвращает новую функцию (обычно обёртку).
def debug(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with", args, kwargs)
result = func(*args, **kwargs)
print(f"{func.__name__} returned", result)
return result
return wrapper
@debug
def add(a, b):
return a + b
add(2, 3)
@debug — синтаксический сахар для add = debug(add). Используйте functools.wraps чтобы сохранить метаданные оригинальной функции:
from functools import wraps
def debug(func):
@wraps(func)
def wrapper(*args, **kwargs):
# ...
return func(*args, **kwargs)
return wrapper
Декораторы применяются для логирования, кеширования (functools.lru_cache), проверки прав, и т.д
Функции с yield возвращают генератор — ленивый итератор.
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for v in count_up_to(3):
print(v)
Генераторы экономят память, позволяют строить бесконечные последовательности, поддерживают send()/throw() API для продвинутого использования.
Можно указывать типы параметров и возвращаемого значения (не обязательны):
def greet(name: str) -> str:
return "Hello " + name
Type hints полезны для статической проверки (mypy), автодополнения в IDE и документации.
Рекурсия — это когда функция вызывает сама себя. Идея: сложную задачу можно разбить на подзадачи того же типа, но меньшего размера.
Любая корректная рекурсивная функция состоит из двух частей:
- Базовый случай — условие выхода из рекурсии. Это момент, когда функция больше не вызывает сама себя.
- Рекурсивный вызов — шаг, в котором функция вызывает себя с изменёнными аргументами, приближая задачу к базовому случаю.
Факториал n! = n × (n−1)!, при этом 0! = 1.
def factorial(n):
if n == 0: # 1 Базовый случай
return 1
else: # 2 Рекурсивный случай
return n * factorial(n - 1)
print(factorial(5)) # 120
factorial(5)
→ 5 * factorial(4)
→ 5 * 4 * factorial(3)
→ 5 * 4 * 3 * factorial(2)
→ 5 * 4 * 3 * 2 * factorial(1)
→ 5 * 4 * 3 * 2 * 1
= 120
Python хранит каждый вызов функции в стеке вызовов.
Когда функция вызывает себя, создаётся новая копия её локальных переменных.
После завершения функции результат возвращается "вверх по стеку".
factorial(3)
├─ factorial(2)
│ ├─ factorial(1)
│ └─ return 1
└─ return 2 * 1 = 2
return 3 * 2 = 6
Если функция вызывает себя бесконечно, стек переполняется:
def bad_recursion():
return bad_recursion()
bad_recursion()
Ошибка:
RecursionError: maximum recursion depth exceeded
В Python по умолчанию глубина рекурсии ограничена (обычно ~1000 вызовов). Проверить можно:
import sys
sys.getrecursionlimit()
- структура данных естественно рекурсивна (деревья, графы, директории);
- решение задачи естественно описывается через себя (Фибоначчи, факториал);
- требуется чистый и читаемый код, а не максимальная производительность.
- глубина может быть очень большой (например, обход длинных списков);
- время или память критичны — итерация часто быстрее и безопаснее.