Добрый день, уважаемые студенты! Сегодня мы будем говорить о функциях в Python, одной из самых важных концепций в программировании. Функции позволяют нам организовать код, сделать его более читаемым, модульным и повторно используемым. Давайте начнем с основ.
Функция - это блок кода, который можно вызывать многократно для выполнения определенной задачи. Функции позволяют
абстрагировать детали реализации и сделать код более структурированным. В Python функции объявляются с использованием
ключевого слова def
, за которым следует имя функции и круглые скобки с параметрами. Например:
def greet(name):
print("Привет,", name)
Здесь мы объявили функцию greet
с одним параметром name
.
Для вызова функции используется имя функции, за которым следуют круглые скобки с передачей аргументов (значений параметров). Например:
greet("Анна")
Этот вызов функции выведет на экран "Привет, Анна".
Функции могут возвращать значения с помощью ключевого слова return
. Например:
def add(x, y):
result = x + y
return result
Вызов add(3, 5)
вернет результат сложения 3 и 5, который можно сохранить в переменной или использовать в других
выражениях.
Функции у которых явно не указан return
будут интерпретироваться питоном как функция в которой последней инструкцией
написано return None
, потому что у функции всегда должно быть возвращаемое значение.
Переменные, объявленные внутри функции, называются локальными и видны только внутри этой функции. Попробуем это продемонстрировать на примере:
def multiply(a, b):
result = a * b
return result
c = 2
d = 3
product = multiply(c, d)
print(result) # Ошибка! Переменная result не видна за пределами функции
В этом примере переменная result
видна только внутри функции multiply
.
Python позволяет указывать значения по умолчанию для аргументов функции. Это позволяет вызывать функцию с меньшим количеством аргументов, если значения по умолчанию заданы. Например:
def power(base, exponent=2):
result = base ** exponent
return result
print(power(3)) # Выведет 9, так как exponent по умолчанию равен 2
print(power(2, 3)) # Выведет 8, так как мы явно указали значение exponent
Python - это язык с динамической типизацией, что означает, что типы переменных определяются автоматически во время выполнения программы. Однако, начиная с версии Python 3.5, можно использовать аннотации типов для объявления ожидаемых типов аргументов и возвращаемых значений функции. Это делает код более читаемым и помогает IDE и инструментам статического анализа проводить проверку типов. Например:
def add(x: int, y: int) -> int:
result = x + y
return result
Здесь мы аннотировали аргументы x
и y
как int
, а возвращаемое значение как int
.
Давайте добавим информацию о передаче функциям случайного количества параметров с использованием *args
и **kwargs
.
В Python вы можете передавать функциям аргументы, количество которых может варьироваться. Для этого используются два специальных синтаксиса:
-
*args
: Этот синтаксис позволяет передавать произвольное количество аргументов в виде кортежа (tuple). Имяargs
является соглашением, но вы можете использовать любое имя после*
. -
**kwargs
: Этот синтаксис позволяет передавать произвольное количество именованных аргументов в виде словаря ( dictionary). Имяkwargs
также является соглашением, но можно использовать любое имя после**
.
def print_args(*args):
for arg in args:
print(arg)
print_args(1, 2, 3, "hello") # Выведет все переданные аргументы
В этом примере *args
собирает все переданные аргументы в кортеж args
, который затем можно перебрать в цикле.
def print_kwargs(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_kwargs(name="John", age=25, city="New York") # Выведет все переданные именованные аргументы
Здесь **kwargs
собирает все переданные именованные аргументы в словарь kwargs
, который можно перебрать в цикле.
Вы также можете комбинировать *args
и **kwargs
в одной функции, но *args
должен идти перед **kwargs
:
def print_all_args_and_kwargs(arg1, *args, kwarg1="default", **kwargs):
print("Обязательный аргумент:", arg1)
print("Дополнительные аргументы (*args):", args)
print("Именованный аргумент (kwarg1):", kwarg1)
print("Дополнительные именованные аргументы (**kwargs):", kwargs)
print_all_args_and_kwargs("first", "second", "third", kwarg1="custom", key1="value1", key2="value2")
В этом примере функция print_all_args_and_kwargs
принимает один обязательный аргумент, произвольное количество
аргументов *args
, один именованный аргумент kwarg1
со значением по умолчанию, и произвольное количество именованных
аргументов **kwargs
. Это позволяет гибко работать с разными видами аргументов при вызове функции.
Использование *args
и **kwargs
может быть полезным, когда вам нужно создавать более гибкие функции, способные
обрабатывать разное количество и типы аргументов.
Давайте добавим информацию о распаковке кортежей с использованием оператора звездочки *
в Python.
Оператор *
позволяет распаковать элементы кортежа или списка и передать их как отдельные аргументы функции. Это
полезно, когда у вас есть кортеж (или список) с переменным количеством элементов, и вы хотите передать их в функцию,
которая ожидает отдельные аргументы.
def multiply(a, b):
return a * b
values = (2, 3)
result = multiply(*values) # Распаковываем кортеж и передаем его элементы как аргументы функции
print(result) # Выведет 6, так как 2 * 3 = 6
В этом примере мы объявили функцию multiply
, которая принимает два аргумента. Затем мы создали кортеж values
с двумя
элементами и использовали оператор *
для распаковки кортежа и передачи его элементов как аргументы функции multiply
.
def add(a, b, c):
return a + b + c
numbers = [1, 2, 3]
result = add(*numbers) # Распаковываем список и передаем его элементы как аргументы функции
print(result) # Выведет 6, так как 1 + 2 + 3 = 6
В этом примере мы используем список numbers
и также распаковываем его элементы как аргументы функции add
.
Распаковка с использованием *
может быть полезной, когда вам нужно передать переменное количество аргументов функции
или когда вы работаете с данными, хранящимися в кортежах или списках. Это делает ваш код более гибким и читаемым.
Давайте добавим информацию о распаковке словарей с использованием оператора двойной звездочки **
в Python.
Оператор **
позволяет распаковать словарь и передать его элементы как именованные аргументы функции. Это полезно,
когда у вас есть словарь с переменным количеством ключей и значениями, и вы хотите передать их в функцию, которая
ожидает именованные аргументы.
def print_person_info(name, age):
print(f"Имя: {name}, Возраст: {age}")
person_info = {"name": "John", "age": 30}
print_person_info(**person_info) # Распаковываем словарь и передаем его элементы как именованные аргументы функции
В этом примере мы объявили функцию print_person_info
, которая принимает два именованных аргумента (name
и age
).
Затем мы создали словарь person_info
с ключами "name"
и "age"
и их соответствующими значениями. С помощью
оператора **
мы распаковываем словарь и передаем его элементы как именованные аргументы функции print_person_info
.
Вы также можете комбинировать *args
и **kwargs
в одной функции, чтобы обработать как позиционные, так и именованные
аргументы.
def print_info(*args, **kwargs):
for arg in args:
print(arg)
for key, value in kwargs.items():
print(f"{key}: {value}")
values = (1, 2, 3)
info = {"name": "John", "age": 30}
print_info(*values, **info) # Распаковываем кортеж и словарь и передаем их элементы как аргументы функции
В этом примере функция print_info
принимает как позиционные аргументы, так и именованные аргументы, используя *args
и **kwargs
.
Распаковка с использованием **
может быть полезной, когда вам нужно передавать переменное количество именованных
аргументов функции или когда вы работаете с данными, хранящимися в словарях. Это делает ваш код более гибким и удобным
для работы с разными видами данных.
Лямбда-функции (или анонимные функции) - это специальный вид функций, которые могут быть определены в одной строке без
использования ключевого слова def
. Они часто используются для создания коротких функций, которые передаются в качестве
аргументов другим функциям. Например:
square = lambda x: x ** 2
print(square(5)) # Выведет 25
Лямбда-функции полезны, когда требуется передать небольшую функцию в функцию высшего порядка, такую как map
, filter
или sorted
.
Конечно, добавлю информацию о передаче изменяемых типов данных в функцию и их влиянии на оригинальные объекты.
В Python существуют два типа данных: изменяемые (mutable) и неизменяемые (immutable). Примерами изменяемых типов данных являются списки (list) и словари (dict), а неизменяемых - целые числа (int), строки (str) и кортежи (tuple).
При передаче изменяемых типов данных в функцию важно понимать, что функция может изменить сам объект, который был передан в качестве аргумента. Это происходит потому, что изменяемые объекты передаются по ссылке, а не по значению.
Рассмотрим пример:
def modify_list(my_list):
my_list.append(4)
original_list = [1, 2, 3]
modify_list(original_list)
print(original_list) # Выведет [1, 2, 3, 4]
В этом примере мы передали список original_list
в функцию modify_list
, и функция добавила элемент 4 в этот список.
После вызова функции original_list
был изменен и теперь содержит элемент 4.
Чтобы избежать таких побочных эффектов, можно передавать изменяемые объекты в функции с помощью копии объекта или
использовать методы копирования, например, copy.copy()
или copy.deepcopy()
из модуля copy
.
import copy
def modify_list_safely(my_list):
new_list = copy.copy(my_list)
new_list.append(4)
return new_list
original_list = [1, 2, 3]
modified_list = modify_list_safely(original_list)
print(original_list) # Выведет [1, 2, 3]
print(modified_list) # Выведет [1, 2, 3, 4]
Таким образом, при работе с изменяемыми объектами важно быть осторожными и учитывать, как изменения в функции могут повлиять на оригинальные объекты.
Для работы с изменяемыми объектами и избежания неожиданных побочных эффектов в Python можно использовать функции copy
и deepcopy
из модуля copy
. Давайте рассмотрим их подробнее.
Модуль copy
предоставляет функцию copy.copy()
, которая позволяет создавать поверхностные копии объектов. Это
означает, что она создает новый объект, который является копией оригинала, но не рекурсивно копирует все вложенные
объекты. Вложенные объекты по-прежнему будут ссылаться на одни и те же данные.
import copy
original_list = [1, 2, [3, 4]]
copied_list = copy.copy(original_list)
print(original_list) # Выведет [1, 2, [3, 4]]
print(copied_list) # Выведет [1, 2, [3, 4]]
# Изменим вложенный список в копии
copied_list[2][0] = 99
print(original_list) # Выведет [1, 2, [99, 4]]
print(copied_list) # Выведет [1, 2, [99, 4]]
Как видно из примера, изменение вложенного списка в копии также затрагивает оригинал. Это происходит потому, что копия создается только на верхнем уровне, а вложенные объекты остаются общими для оригинала и копии.
Для создания глубоких копий объектов, включая все вложенные объекты, используйте функцию copy.deepcopy()
. Глубокая
копия создает новую структуру данных, которая полностью независима от оригинала.
import copy
original_list = [1, 2, [3, 4]]
deep_copied_list = copy.deepcopy(original_list)
print(original_list) # Выведет [1, 2, [3, 4]]
print(deep_copied_list) # Выведет [1, 2, [3, 4]]
# Изменим вложенный список в глубокой копии
deep_copied_list[2][0] = 99
print(original_list) # Выведет [1, 2, [3, 4]]
print(deep_copied_list) # Выведет [1, 2, [99, 4]]
Как видно из примера, изменения во вложенном списке в глубокой копии не влияют на оригинальный список. Это позволяет безопасно работать с вложенными объектами и избегать неожиданных изменений в оригинальных данных.
Итак, функции copy.copy()
и copy.deepcopy()
в модуле copy
предоставляют удобные средства для копирования объектов
с учетом их изменяемости и вложенности. Выбор между ними зависит от вашего конкретного случая использования.
Конечно, давайте рассмотрим использование функций map
, zip
, filter
и reduce
в Python для манипуляции данными и
работы с последовательностями.
Функция map
используется для применения определенной функции к каждому элементу в итерируемой последовательности (
например, списку) и создания новой последовательности с результатами. Это позволяет применять одну функцию к нескольким
элементам без явного использования циклов. Пример:
def square(x):
return x ** 2
numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(square, numbers))
print(squared_numbers) # Выведет [1, 4, 9, 16, 25]
В этом примере мы создали функцию square
, которая возводит число в квадрат, и применили её ко всем элементам
списка numbers
с помощью map
.
Функция zip
позволяет объединить несколько итерируемых последовательностей в одну последовательность кортежей.
Количество элементов в результирующей последовательности равно минимальному количеству элементов среди всех переданных
последовательностей. Пример:
names = ["Анна", "Иван", "Мария"]
scores = [90, 85, 88]
zipped_data = list(zip(names, scores))
print(zipped_data) # Выведет [('Анна', 90), ('Иван', 85), ('Мария', 88)]
Здесь мы объединили список имен и список оценок в список кортежей, создавая пары "имя - оценка".
Функция filter
используется для фильтрации элементов в итерируемой последовательности на основе заданного условия (
функции). Она возвращает только те элементы, для которых условие истинно. Пример:
def is_even(x):
return x % 2 == 0
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = list(filter(is_even, numbers))
print(even_numbers) # Выведет [2, 4, 6]
Здесь мы определили функцию is_even
, которая проверяет, является ли число четным, и использовали filter
, чтобы
отфильтровать только четные числа из списка numbers
.
Использование функций map
, zip
, filter
делает код более читаемым и позволяет выполнять разнообразные операции с
данными в более функциональном стиле.
Конечно, давайте добавим информацию о рекурсии в лекцию.
Рекурсия - это концепция в программировании, при которой функция вызывает саму себя внутри своего тела. Это мощный инструмент, который позволяет решать задачи, которые могут быть разбиты на более мелкие подзадачи того же типа. Рекурсия в Python работает аналогично рекурсии в математике.
-
Базовый случай (Base Case): Это условие, при котором рекурсия завершается и функция больше не вызывает саму себя. Без базового случая рекурсивная функция будет вызывать себя бесконечно.
-
Рекурсивный случай (Recursive Case): Это условие, при котором функция вызывает саму себя для решения более мелкой подзадачи. Рекурсивный случай должен быть сформулирован так, чтобы в конечном итоге привести к базовому случаю.
Давайте рассмотрим пример рекурсивной функции для вычисления факториала числа. Факториал числа n
(обозначается
как n!
) - это произведение всех положительных целых чисел от 1 до n
.
def factorial(n):
# Базовый случай: факториал 0 или 1 равен 1
if n == 0 or n == 1:
return 1
# Рекурсивный случай: вычисляем факториал для (n-1) и умножаем на n
else:
return n * factorial(n - 1)
# Вызываем функцию для вычисления факториала числа 5
result = factorial(5)
print(result) # Выведет 120, так как 5! = 5 * 4 * 3 * 2 * 1 = 120
Преимущества:
- Рекурсия может сделать код более читаемым и интуитивно понятным, особенно для задач, связанных с древовидными или рекурсивными структурами данных.
- Она может предоставить более лаконичное и элегантное решение для некоторых задач.
Ограничения:
- Рекурсия может быть менее эффективной по сравнению с итеративными методами в некоторых случаях из-за накладных расходов на вызов функций.
- Слишком глубокая рекурсия может вызвать переполнение стека вызовов (stack overflow), что приведет к ошибке.
При использовании рекурсии важно правильно формулировать базовый и рекурсивный случаи, чтобы функция завершилась и не вошла в бесконечный цикл.
Задачи
Задача 1: Сумма чисел
Напишите функцию calculate_sum, которая принимает два аргумента (числа) и возвращает их сумму. Затем вызовите функцию и выведите результат.
Задача 2: Расчет среднего значения
Напишите функцию calculate_average, которая принимает список чисел в качестве аргумента и возвращает среднее значение этих чисел. Затем вызовите функцию с разными списками чисел.
Задача 3: Факториал числа
Напишите функцию factorial, которая принимает целое число в качестве аргумента и возвращает его факториал (произведение всех положительных целых чисел от 1 до n). Вызовите функцию для вычисления факториалов нескольких чисел.
Задача 4: Фильтрация списка
Напишите функцию filter_even, которая принимает список чисел и возвращает список, содержащий только четные числа из исходного списка. Затем вызовите функцию с разными списками чисел.
Задача 5: Палиндром
Напишите функцию is_palindrome, которая принимает строку в качестве аргумента и возвращает True, если строка является палиндромом (читается одинаково слева направо и справа налево), и False в противном случае. Проверьте функцию на различных строках.
Задача 6: Работа со списками
Напишите функцию modify_list, которая принимает список чисел и возвращает новый список, содержащий квадраты чисел из исходного списка. Затем вызовите функцию и проверьте её работу.
Задача 7: Фибоначчи
Напишите функцию fibonacci, которая принимает число n в качестве аргумента и возвращает список первых n чисел Фибоначчи. Проверьте функцию для разных значений n.