Функция - именованный фрагмент кода, отделенный от других.
Определение функции
Пример функции, которая ничего не делает:
Python требует использовать выражение pass для демонстрации того, что функция ничего не делает.
Вызов функции с помощью скобок
Usage in if:
P.S. с помощью return можно возвращать несколько значений в виде кортежа:
Аргументы и параметры
Значения вне функции называются аргументами, а значения внутри - параметрами.
Значения, передаваемые в функцию при вызове, называются аргументами. Когда вызываешь функцию с аргументами, значения этих аргументов копируются в соответствующие параметры внутри функций.
None
None - специальное значение в Python - появляется, если функция ничего не возвращает. Не является булевым False.
Чтобы отличить None от булева значения False, юзай оператор is:
В чем тогда разница
None потребуется, чтобы различать отсутствующие значения и пустые. int нули 0, float нули 0.0, пустые строки '', списки [], кортежи (,),
словари {} и множества set() равны False, но это не то же самое, что None.
Такая функция выводит на экран, является ли ее аргумент None, True или False:
Аргументы
Пример чисто позиционных аргументов:
Минус в том, что нужно в правильном порядке передавать аргументы.
Можно использовать такой подход, не парясь о порядке передаваемых аргументов:
Либо комбинируя:
Дефолтное значение параметра
Дефолтное значение параметров высчитывается, когда функция определяется, а не выполняется.
Дефолтное значение указывается при определении функции:
Нельзя использовать изменяемый тип данных в качестве значения параметра по умолчанию (списки, словари и т.д.). На примере багованной функции будет показано “почему нельзя?”, которая, по идее, каждый раз должна запускаться с новым пустым списком result, добавлять в него аргумент arg, а затем выводить на экран список, состоящий из одного элемента. Однако в этой функции есть баг: список пуст только при первом вызове. Во второй раз список result будет содержать элемент, оставшийся после предыдущего вызова:
Рабочий вариант:
Решить проблему можно, передав в функцию что-либо еще, указывающее на то,
что вызов является первым:
(btw, популярный вопрос на собесах Python-разработчиков)
*args - аргументы в кортеж
Если символ * будет использован внутри функции с параметром (*args), произвольное кол-во позиционных аргументов сгруппируется в один кортеж.
Аналог $@ / $* в bash. (silly🤪 аналог)
Не обязательно называть кортеж args (как и **kwargs), но это распространенная идиома в Python, поэтому не выебываемся.
Более распространенный пример:
Символ * можно использовать только при описании функции и ее вызове:
**kwargs - аргументы в словарь
Можно юзать два астериска (**), чтобы сгруппировать аргументы - ключевые слова в словарь, где имена аргументов станут ключами, а их значения - соответствующими значениями в словаре:
В итоге порядок аргументов будет следующим:
Обязательные позиционные аргументы
Необязательные позиционные аргументы (*args)
Необязательные аргументы - ключевые слова (**kwargs)
Синтаксис ** можно применять только при вызове функции или ее определении:
Аргументы, передаваемые только по ключевым словам
Python 3 позволяет указывать аргументы, которые передаются только по ключевым словам. Их нужно предоставлять в форматеимя=значение, а не позиционно, как значение. Знак * в определении функции говорит о том, что параметры start и endнужно предоставлять как именованные аргументы, если не хочешь использовать их значения по умолчанию:
Изменяемые и неизменяемые аргументы
Если аргумент изменяемые, то его значение можно изменить изнутри функции с помощью соответствующего параметра:
Лучше избегать таких ситуаций.
Документация функции __doc__
Можно (нужно) прикрепить документацию к определению функции, включив в начало ее тела строку, которая и называется строкой документации:
Многострочная документация:
dunder
Строка doc является внутренним именем строки документации как переменной внутри функции. Двойное нижнее подчеркивание (на сленге - dunder (double under)) часто используется для именования внутренних переменных Python, поскольку программисты, скорее всего, не будут использовать подобные имена переменных.
Функция - тоже объект первого класса
В Python все является объектом (числа, строки, кортежи, списки, словари и даже функции).
Функции Python - это объекты первого класса⇒ их можно присваивать переменным, хранить в структурах данных, передавать в качестве аргументов другим функциям и даже возвращать в качестве значений из других функций.
Хитрость (?):
Роль круглых скобок
Функция answer() была передана в качестве аргумента функции run_something()без круглых скобок (run_something(answer)). В Python круглые скобки означают “вызови эту функцию”. Если скобок нет, Python относится к функции как к любому другому объекту. Это происходит потому, что функция тоже является объектом.
btw, можно сделать то же самое с *args и **kwargs:
Внутренние функции
Можно определить функцию внутри другой функции:
Замыкания
Замыкание - функция, которая динамически генерируется другой функцией.
Обе они могут изменяться и запоминать значения переменных, которые были созданы вне функции.
Здесь:
inner2() использует внешний параметр saying directly, вместо того чтобы получить его как аргумент
knights2() возвращает имя функции inner2, вместо того чтобы вызывать ее
Функция inner2() знает значение переменой saying, которое было передано в функцию, и запоминает его. Строка inner2 возвращает эту особую копию функции inner2 (но не вызывает ее). Получается замыкание: динамически созданная функция, которая запоминает, откуда она появилась.
Наглядно:
Пространства имен (namespaces) и область определения
Программы на Python имеют различные пространства имен (namespaces) - разделы, внутри которых определенное имя уникально и не связано с таким же именем в других пространствах имен.
Каждая функция определяет собственное пространство имен. Если определить переменную с именем var в основной программе и другую переменную var в отдельной функции, они будут ссылаться на разные значения.
Но получить доступ к именам в других namespaces можно разными способами.
В основной программе определяется глобальный namespace, поэтому переменные, находящиеся в нем, являются глобальными.
Значение глобальной переменной можно спокойно получить внутри функции:
Но получить и изменить значение глобальной переменной внутри функции не получится:
Если просто изменить - изменится другая переменная с тем же именем, находящаяся внутри функции:
Чтобы получить доступ конкретно к глобальной переменной, нужно явно юзать ключевое слово global:
Что, если не указать ключевое слово global
Если не использовать ключевое слово global внутри функции, Python задействует локальное пространство имен ⇒переменная будет локальной и пропадет после того, как функция завершит работу.
В Python есть две функции для доступа к содержимому пространств имен:
locals() - возвращает словарь с именами локального пространства
имен
globals() - возвращает словарь, содержащий имена глобального пространства
имен.
Использование _ и __ в именах
Имена, которые начинаются с двух нижних подчеркиваний __ и заканчиваются ими, зарезервированы для использования внутри Python, поэтому не надо их юзать в именах своих переменных.
btw, основной программе присвоено спец. имя __main__
Демонстрация этих dunder переменных (🤷♂️):
Рекурсия
Рекурсия, in this case - функция, вызывающая сама себя.
Бесконечная рекурсия, естественно, не нужна, поэтому даже Python для такого случая генерирует исключение:
Но рекурсия может быть полезна для работы с вложенными списками и т.д.:
btw, в Python 3.3 появилось выражение yield from, которая позволяет генератору передавать часть работы другому генератору ⇒ можно упростить функцию flatten() выше:
С помощью рекурсии можно написать функцию, которая находит факториал от числа (nerdy ass tasks):
Или реализовать другой поиск элемента в сложном словаре: