Как правильно объявлять функции в Python: синтаксис и примеры

Раздел: Основы Python -> Определение функций

Основные подходы к созданию функций

Наиболее распространённый и эффективный способ определить функцию в Python - использовать ключевое слово def, имя функции (например, f), круглые скобки с параметрами и двоеточие. Тело функции записывается с отступом в 4 пробела. Это позволяет многократно выполнять один и тот же блок кода, передавая разные аргументы.

def f(): 
    print("Привет, мир!")

f()

Def f python (определение функции f в python)

Привет, мир!

Def print python (определение функции print в python)

Такая конструкция не принимает аргументов и ничего не возвращает (возвращает None). Если требуется вернуть значение, используется оператор return.

Типичная ошибка - забыть двоеточие после заголовка функции или нарушить отступы. Python вызовет SyntaxError или IndentationError.

Как определить функцию с параметрами?

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

def greet(name):
    print(f"Привет, {name}!")

greet("Анна")
Привет, Анна!
Если число аргументов при вызове не совпадает с числом параметров, возникает TypeError.

Как вернуть значение из функции?

Ключевое слово return завершает выполнение функции и передаёт объект обратно. Функция может вернуть одно значение, несколько (в виде кортежа) или ничего (вернётся None).

def add(a, b):
    return a + b

result = add(3, 5)
print(result)
8

Если return не указан, функция возвращает None.

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

Как задать параметры со значениями по умолчанию?

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

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

print(power(3))      # 9
print(power(3, 3))   # 27
9
27
Опасность: значение по умолчанию вычисляется один раз в момент определения функции. Если используется изменяемый объект (список, словарь), он будет общим для всех вызовов. Исправление: использовать None как значение по умолчанию и создавать новый объект внутри функции.

Как передать произвольное количество аргументов?

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

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

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print(sum_all(1, 2, 3, 4))   # 10
print_info(name="Анна", age=25)
10
name: Анна
age: 25
Путаница между *args и **kwargs - порядок важен: сначала обычные параметры, затем *args, потом **kwargs. Нарушение порядка приведёт к синтаксической ошибке.

Как создать анонимную функцию (лямбда)?

Лямбда-выражение позволяет определить короткую функцию без def. Она содержит одно выражение, результат которого возвращается.

square = lambda x: x ** 2
print(square(5))  # 25

# Использование с filter
numbers = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)      # [2, 4]
25
[2, 4]
Лямбда-функции не поддерживают многострочное тело или инструкции. Для сложной логики следует использовать обычный def.

Как использовать аннотации типов?

Аннотации указывают ожидаемые типы параметров и возвращаемого значения. Они не влияют на выполнение, но помогают инструментам статического анализа и документируют код.

def multiply(x: int, y: int) -> int:
    return x * y

print(multiply(3, 4))  # 12
Аннотации не проверяются во время исполнения. Если передать строку, Python не выдаст ошибку, но может возникнуть исключение внутри тела.

Как определить вложенную (замыкающую) функцию?

Функция может быть объявлена внутри другой функции. Внутренняя функция видит переменные внешней (замыкание).

def make_adder(x):
    def adder(y):
        return x + y
    return adder

add5 = make_adder(5)
print(add5(3))  # 8
Ошибки возникают, если попытаться изменить нелокальную переменную без объявления nonlocal.

Как применить рекурсию?

Функция может вызывать саму себя. Обязательно должно быть базовое условие для остановки.

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))  # 120
Рекурсия без базового условия вызывает бесконечную рекурсию и ошибку RecursionError. Глубина рекурсии ограничена (обычно 1000).

Как создать функцию-генератор?

Вместо return используется yield. Функция возвращает объект-генератор, который выдаёт значения по одному.

def count_up_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for num in count_up_to(3):
    print(num)
1
2
3
Генератор можно использовать только один раз. После исчерпания он перестаёт выдавать значения.

Как оформить документацию внутри функции?

Первая строка тела в виде строки (docstring) служит документацией. Она доступна через атрибут __doc__.

def my_function():
    """Эта функция ничего не делает, но документирована."""
    pass

print(my_function.__doc__)
Эта функция ничего не делает, но документирована.
Если docstring отсутствует, __doc__ возвращает None. Рекомендуется всегда документировать функции.

Расширенные примеры

Функция с изменяемым аргументом по умолчанию (ловушка)

Следующая функция демонстрирует неожиданное поведение при использовании изменяемого объекта как значения по умолчанию.

Пример
def append_to(elem, target=[]):
    target.append(elem)
    return target

print(append_to(1))  # [1]
print(append_to(2))  # [1, 2], а не [2]
print(append_to(3, []))  # [3]
[1]
[1, 2]
[3]

Правильный способ - использовать None и создавать новый список внутри:

Пример
def append_to_fixed(elem, target=None):
    if target is None:
        target = []
    target.append(elem)
    return target

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

Декоратор для логирования времени выполнения

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

Пример
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"Функция {func.__name__} выполнялась {elapsed:.4f} секунд")
        return result
    return wrapper

@timer
def slow_sum(a, b):
    time.sleep(1)
    return a + b

print(slow_sum(3, 4))
Функция slow_sum выполнялась 1.0002 секунд
7

Генератор для чисел Фибоначчи

Генераторная функция экономит память, создавая последовательность по запросу.

Пример
def fibonacci(limit):
    a, b = 0, 1
    for _ in range(limit):
        yield a
        a, b = b, a + b

for fib in fibonacci(10):
    print(fib, end=' ')
0 1 1 2 3 5 8 13 21 34 

Функция с проверкой типов через декоратор

Собственный декоратор может проверять аргументы на соответствие аннотациям.

Пример
def validate(func):
    def wrapper(*args, **kwargs):
        import inspect
        sig = inspect.signature(func)
        bound = sig.bind(*args, **kwargs)
        for name, value in bound.arguments.items():
            expected = sig.parameters[name].annotation
            if expected is not inspect.Parameter.empty and not isinstance(value, expected):
                raise TypeError(f"Аргумент {name} должен быть {expected.__name__}, а не {type(value).__name__}")
        return func(*args, **kwargs)
    return wrapper

@validate
def divide(x: int, y: int) -> float:
    return x / y

print(divide(10, 3))     # работает
# print(divide(10, "3")) # TypeError
3.3333333333333335

Определение функции f в Python - comments

En
Def f python (python)