Как именовать функции в python
Функции и их аргументы
В этой статье я планирую рассказать о функциях, именных и анонимных, инструкциях def, return и lambda, обязательных и необязательных аргументах функции, функциях с произвольным числом аргументов.
Именные функции, инструкция def
Определим простейшую функцию:
Инструкция return говорит, что нужно вернуть значение. В нашем случае функция возвращает сумму x и y.
Теперь мы ее можем вызвать:
Функция может быть любой сложности и возвращать любые объекты (списки, кортежи, и даже функции!):
Функция может и не заканчиваться инструкцией return, при этом функция вернет значение None:
Аргументы функции
Функция может принимать произвольное количество аргументов или не принимать их вовсе. Также распространены функции с произвольным числом аргументов, функции с позиционными и именованными аргументами, обязательными и необязательными.
Функция также может принимать переменное количество позиционных аргументов, тогда перед именем ставится *:
Функция может принимать и произвольное число именованных аргументов, тогда перед именем ставится **:
В переменной kwargs у нас хранится словарь, с которым мы, опять-таки, можем делать все, что нам заблагорассудится.
Анонимные функции, инструкция lambda
Анонимные функции могут содержать лишь одно выражение, но и выполняются они быстрее. Анонимные функции создаются с помощью инструкции lambda. Кроме этого, их не обязательно присваивать переменной, как делали мы инструкцией def func():
lambda функции, в отличие от обычной, не требуется инструкция return, а в остальном, ведет себя точно так же:
Соглашения об именах в Python
Как называть переменные и объекты в коде
Соглашения об именах библиотеки Python немного беспорядочные, поэтому мы никогда не получим полное согласование. Тем не менее, вот рекомендуемые в настоящее время стандарты именования. Новые модули и пакеты (включая сторонние фреймворки) должны быть написаны в этих стандартах, но там, где существующая библиотека имеет другой стиль, внутренняя согласованность предпочтительнее.
Главный принцип: Имена, которые видны пользователю как открытые части API, должны соответствовать соглашениям, которые отражают использование, а не реализацию.
Описание стилей именования в языках программирования:
Есть много разных стилей именования. Это помогает понять, какой стиль именования используется независимо от того, для чего он используется.
Следующие стили именования обычно различаются:
Библиотека X11 использует ведущий X для всех своих публичных функций. В Python этот стиль обычно считается ненужным, потому что имена атрибутов и методов начинаются с префикса объекта, а имена функций начинаются с имени модуля.
Кроме того, распознаются следующие специальные формы с использованием начальных или конечных подчеркиваний (обычно они могут сочетаться с любым соглашением по случаю):
_single_leading_underscore : для внутреннего использования. Например, from M import * не импортируются объекты, имена которых начинаются с подчеркивания.
single_trailing_underscore_ : используется по соглашению, чтобы избежать конфликтов с ключевыми словами Python, например
__double_leading_underscore : изменяет имя атрибута класса (внутри класса FooBar __boo становится _FooBar__boo ).
Соглашения об именах в Python.
Имена, которых следует избегать:
Никогда не используйте символы l (строчная буква L ), O (заглавная буква o ) или I (заглавная буква i ) в качестве имен переменных из одного символа.
Совместимость с ASCII:
Идентификаторы, используемые в стандартной библиотеке должны быть ASCII совместимы, как описано в разделе политики в PEP 3131.
Имена пакетов и модулей:
Модули должны иметь короткие, строчные имена. Подчеркивания могут использоваться в имени модуля, если это улучшает читабельность. Пакеты Python также должны иметь короткие, строчные имена, хотя использование подчеркивания не рекомендуется.
Когда модуль расширения, написанный на C или C++, имеет сопровождающий модуль Python, который содержит интерфейс более высокого уровня (например, более объектно-ориентированный), модуль начинается с символа подчеркивания (например _socket ).
Имена классов:
Соглашение об именовании для функций может использоваться вместо этого в случаях, когда интерфейс задокументирован и используется в основном как вызываемый.
Обратите внимание, что для встроенных имен существует отдельное соглашение: большинство встроенных имен представляют собой отдельные слова (или два слова, идущие вместе), причем соглашение CapWords используется только для имен исключений и встроенных констант.
Имена типов переменных:
Имена исключений:
Поскольку исключения должны быть классами, здесь применяется соглашение об именах классов. Однако вы должны использовать суффикс Error в именах исключений (если исключение на самом деле является ошибкой).
Имена глобальных переменных:
Будем надеяться, что эти переменные предназначены для использования только внутри одного модуля. Соглашения примерно такие же, как и для функций.
Имена функций:
Имена функций должны быть строчными, слова должны быть разделены подчеркиванием, чтобы улучшить читаемость.
Имена переменных следуют тому же соглашению, что и имена функций.
mixedCase допускается только в тех случаях, когда уже преобладает такой стиль, для сохранения обратной совместимости.
Аргументы функций и методов:
Всегда используйте self в качестве первого аргумента для методов экземпляра.
Всегда используйте cls в качестве первого аргумента для методов класса.
Имена методов и переменных экземпляра класса:
При необходимости используйте правила именования функций: строчные буквы со словами, разделенными подчеркиванием, чтобы улучшить читаемость.
Используйте одно начальное подчеркивание только для закрытых методов и переменных экземпляра.
Чтобы избежать конфликта имен с подклассами, используйте два ведущих подчеркивания для вызова правил искажения имен в Python.
Примечание: есть некоторые противоречия по поводу использования __names (см. Ниже).
Константы:
Проектирование наследований:
Всегда решайте, должны ли методы класса и его экземпляра («атрибуты класса») быть открытыми или закрытыми. В случае сомнений, выберите закрытый. Проще сделать его общедоступным позже, чем сделать общедоступный атрибут закрытым.
Мы не используем термин «приватный», поскольку ни один атрибут не является таковым в Python.
Имея это в виду, вот данные руководящие принципы:
Публичные атрибуты не должны иметь начальных подчеркиваний.
Если имя вашего общедоступного атрибута вступает в противоречие с зарезервированным ключевым словом, добавьте к его имени один завершающий символ подчеркивания. Это предпочтительнее аббревиатуры или искаженного написания. (Однако, несмотря на это правило, cls является предпочтительным написанием для любой переменной или аргумента, который, как известно, является классом, особенно первым аргументом для метода класса.)
Примечание 1: См. Рекомендацию выше имени аргумента для методов класса.
Примечание 1: Свойства properties работают только с классами нового стиля.
Примечание 2: Старайтесь не допускать побочных эффектов функционального поведения, хотя, такие как кэширование, обычно хороши.
Примечание 3: Избегайте использования в свойствах дорогих операций, так как нотация атрибута заставляет пользователя класса думать, что доступ (относительно) дешев.
Если ваш класс предназначен для использования в подклассах (наследование), и у вас есть атрибуты, которые вы не хотите использовать в наследовании, рассмотрите возможность присвоения им имен с двойным нижним подчеркиванием и без завершающего подчеркивания. Это вызывает алгоритм искажения имени Python, где имя класса искажается в имя атрибута. Это помогает избежать конфликтов имен атрибутов, если подклассы непреднамеренно содержат атрибуты с одинаковыми именами.
Примечание 1: Обратите внимание, что в искаженном имени используется только простое имя класса, поэтому, если подкласс выбирает одно и то же имя класса и имя атрибута, вы все равно можете получить конфликты имен.
Примечание 3: не всем нравится искажение имени. Постарайтесь уравновесить необходимость избегать случайных конфликтов имен с потенциальным использованием продвинутыми программистами.
Публичные и внутренние интерфейсы
Любые гарантии обратной совместимости применяются только к общедоступным интерфейсам. Соответственно, важно, чтобы пользователи могли четко различать открытый и внутренний интерфейсы.
Документированные интерфейсы считаются общедоступными, если в документации явно не указано, что они являются временными или внутренними. Такие интерфейсы не гарантируют обратной совместимости. Все недокументированные интерфейсы должны быть внутренними.
Даже при правильной установке __all__ внутренние интерфейсы (пакеты, модули, классы, функции, атрибуты или другие имена) должны по-прежнему иметь префикс с единственным начальным подчеркиванием.
Интерфейс также считается внутренним, если содержащее пространство имен (пакет, модуль или класс) считается внутренним.
Именование в Python — как выбирать имена и почему это важно
Н ачинающие программисты не придают значение именованию переменных при написании кода. Особенно это касается новых проектов — есть какая-то идея, её нужно реализовать и получить результат. Нет времени на то, чтобы задумываться о названиях для переменных, функций, классов и т.п.
Но проект растёт, количество кода увеличивается, и в один прекрасный день разработчик понимает, что 80% его времени уходит на чтение кода и его модификацию (а вовсе не на написание нового кода). Код, написанный неделю назад, кажется незнакомым — приходиться тратить время и силы чтобы вспомнить, что он делает. Плохое именование усугубляет картину:
Минусы такого именования:
Основная суть программирования — это управление сложностью. Поддерживать сотни тысяч строк кода — достаточно сложно. Для уменьшения сложности приложение разбивают на модули, методы и классы. Правильное именование переменных также помогает в управлении сложностью.
Управление сложностью является сущностью компьютерного программирования.
Ниже рассмотрим, как правильно именовать переменные в Python, какую нотацию использовать + кратко рассмотрим стандарт РЕР8.
Допустимые имена переменных в Python
Вы можете придумать любые названия для переменных в Python, но есть несколько правил, которые нужно соблюдать.
Правила для переменных в Python:
Список всех зарезервированных ключевых слов можно посмотреть так:
import keyword print(keyword.kwlist)
Если нарушить правило именования, получим SyntaxError :
3r = 10 print(3r) File «test.py», line 1 3r = 10 ^ SyntaxError: invalid syntax
Нотация в Python: CamelCase или under_score?
Нотация — это соглашение об именовании. Наиболее популярные нотации в программировании — camel case и underscore.
camelCase (еще называется «верблюжья нотация») — это стиль, в котором слова пишутся слитно, а каждое слово начинается с прописной (большой) буквы. Имеется два подвида этого стиля: lowerCamelCase (все слова кроме первого начинаются с прописной буквы) и UpperCamelCase (все слова, в том числе и первое пишутся с большой буквы).
under_score (snake_case) — при использовании этого стиля в качестве разделителя используется символ подчеркивания «_», а все слова начинаются со строчной (маленькой) буквы;
В Python преимущественно используется нотация under_score
Однако under_score — не единственная нотация, рекомендуемая к использованию в Python. Вот гайдлайн по именованию, основанный на рекомендациях Гвидо ван Россума (автора языка Python):
Как выбирать имена для переменных в Python
Основной принцип хорошего именования — имена должны быть содержательными (полностью отражать своё назначение). Перед тем, как дать имя переменной, функции или классу, ответьте на вопросы:
👎 Пример плохого именования:
def area(side1, side2): return side1 * side2 d = area(4, 5)
В данном примере переменная d не обозначает ровным счётом ничего. По названию функции area не понятно, что она делает, какие параметры принимает и что возвращает.
👍 Пример хорошего именования:
def get_rectangle_area(length, width): return length * width area = get_rectangle_area(4, 5)
В примере понятно что делает функция get_rectangle_area (передав длину и ширину, функция вычисляет и возвращает площадь прямоугольника). Результат попадет в переменную area (площадь).
Содержательное имя понятно, легко читается и не требует дополнительных комментариев.
Вот еще несколько рекомендаций по выбору имён в Python:
⚡ Подбирайте имена на английском языке. Не нужно использовать русские имена переменных и писать их транслитом. Для программиста важно знать английский язык.
def proverka_zdorovya(): # плохо pass def health_check(): # хорошо pass
⚡ Для функций используйте глагол (например «купить», «получить», «распечатать»), для переменных, модулей и классов используйте существительное (например «список», «покупатели», «товары»).
Частая ошибок новичков — называть функцию существительным:
def speed_calculator(distance, time): # неправильно (калькулятор скорости) pass def calculate_speed(distance, time): # правильно (рассчитать скорость) pass
⚡ Имена функций должны быть как можно короче. Функция выполняет всего одно действие и именно оно должно отображаться в её имени.
def create_text_utf8_pipe_from_input(): # плохо pass def create_pipe(): # хорошо pass
⚡ Перед именами булевских переменных (и методов, возвращающих тип bool ), добавляйте префикс «is».
def is_goal_state(): # хорошо pass is_active = True # хорошо
⚡ Используйте удобопроизносимые имена. Людям удобно работать со словами. Часть мозга специализируется на обработке слов — нужно использовать эту часть мозга.
# пример плохого именования (имена трудно произносить вслух) class GlobalFRTEV: # плохо def get_any_mdhd(self): # плохо pass def get_any_mdhl(self): # плохо pass def get_any_mdhf(self): # плохо pass
Имена будут использоваться в обсуждении с коллегами. Рассказывать про баг в методе get_any_mdhf класса GlobalFRTEV будет достаточно проблематично.
⚡ Использовать однобуквенные переменные не рекомендуется. Их можно использовать исключительно для локальных переменных в небольших методах.
r = requests.get(‘https://pythonchik.ru’) # плохо response = requests.get(‘https://pythonchik.ru’) # хорошо
⚡ Не нужно разрабатывать и использовать свою систему кодирования имен. Такие имена трудно произносить и в них можно сделать опечатку. К тому же, каждый новый сотрудник должен будет изучать вашу систему кодирования.
tm1_word = «Hello» # плохо
⚡ Также не следует использовать юмор при выборе имени файла. Ведь шутка может быть понятна не всем. Также не рекомендуется использовать каламбуры и сленг.
⚡ Помните, что ваш код будут читать программисты, поэтому используйте слова из профессиональной области, например, названия алгоритмов, паттернов или математические термины. Также полезно использовать слова из предметной области, к которой принадлежит решаемая задача.
💭 Больше полезных сведений о выборе имен идентификаторов и другие советы, которые пригодятся при написании кода, можно найти в книгах:
Именование переменных в PEP8
В стандарте РЕР8 описаны следующие стили именования:
💁♂️ Для удобства, среды разработки (например PyCharm) автоматически проверяют, насколько код соответствует рекомендациям стандарта РЕР8. Если имя идентификатора не будет соответствовать соглашениям, то IDE подчеркнет переменную, а если навести на нее мышку, появится сообщение с подсказкой.
Хороший код документирует сам себя. При правильном именовании, не возникает вопросов по тому, что происходит в отдельных частях кода. И отпадает необходимость писать комментарии к каждой строке.
Чтобы прокачать навык именования переменных, читайте чужой код крупных проектов (например на github). Смотрите, какие имена используют опытные разработчики, анализируйте и применяйте в своих проектах.
Функции в Python
Введение
Определение
Вот пример простой функции:
Для определения функции нужно всего лишь написать ключевое слово def перед ее именем, а после — поставить двоеточие. Следом идет блок инструкций.
Функция инкрементирует глобальную переменную i и возвращает None (по умолчанию).
Вызовы
Для вызова функции, которая возвращает переменную, нужно ввести:
Для вызова функции, которая ничего не возвращает:
Функцию можно записать в одну строку, если блок инструкций представляет собой простое выражение:
Функции могут быть вложенными:
Функции — это объекты, поэтому их можно присваивать переменным.
Инструкция return
Возврат простого значения
Возврат нескольких значений
Пока что функция возвращала только одно значение или не возвращала ничего (объект None). А как насчет нескольких значений? Этого можно добиться с помощью массива. Технически, это все еще один объект. Например:
Аргументы и параметры
В функции можно использовать неограниченное количество параметров, но число аргументов должно точно соответствовать параметрам. Эти параметры представляют собой позиционные аргументы. Также Python предоставляет возможность определять значения по умолчанию, которые можно задавать с помощью аргументов-ключевых слов.
Параметр — это имя в списке параметров в первой строке определения функции. Он получает свое значение при вызове. Аргумент — это реальное значение или ссылка на него, переданное функции при вызове. В этой функции:
x и y — это параметры, а в этой:
При определении функции параметры со значениями по умолчанию нужно указывать до позиционных аргументов:
Если использовать необязательный параметр, тогда все, что указаны справа, должны быть параметрами по умолчанию.
Выходит, что в следующем примере допущена ошибка:
Для вызовов это работает похожим образом. Сначала нужно указывать все позиционные аргументы, а только потом необязательные:
На самом деле, следующий вызов корректен (можно конкретно указывать имя позиционного аргумента), но этот способ не пользуется популярностью:
А этот вызов некорректен:
При вызове функции с аргументами по умолчанию можно указать один или несколько, и порядок не будет иметь значения:
Можно не указывать ключевые слова, но тогда порядок имеет значение. Он должен соответствовать порядку параметров в определении:
Если ключевые слова не используются, тогда нужно указывать все аргументы:
Второй аргумент можно пропустить:
Чтобы обойти эту проблему, можно использовать словарь:
Значение по умолчанию оценивается и сохраняется только один раз при определении функции (не при вызове). Следовательно, если значение по умолчанию — это изменяемый объект, например, список или словарь, он будет меняться каждый раз при вызове функции. Чтобы избежать такого поведения, инициализацию нужно проводить внутри функции или использовать неизменяемый объект:
Еще один пример изменяемого объекта, значение которого поменялось при вызове:
Дабы не допустить изменения оригинальной последовательности, нужно передать копию изменяемого объекта:
Указание произвольного количества аргументов
Позиционные аргументы
При вызове функции нужно вводить команду следующим образом:
Python обрабатывает позиционные аргументы следующим образом: подставляет обычные позиционные аргументы слева направо, а затем помещает остальные позиционные аргументы в кортеж (*args), который можно использовать в функции.
Если лишние аргументы не указаны, значением по умолчанию будет пустой кортеж.
Произвольное количество аргументов-ключевых слов
Как и в случае с позиционными аргументами можно определять произвольное количество аргументов-ключевых слов следующим образом (в сочетании с произвольным числом необязательных аргументов из прошлого раздела):
При вызове функции нужно писать так:
Python обрабатывает аргументы-ключевые слова следующим образом: подставляет обычные позиционные аргументы слева направо, а затем помещает другие позиционные аргументы в кортеж (*args), который можно использовать в функции (см. предыдущий раздел). В конце концов, он добавляет все лишние аргументы в словарь (**kwargs), который сможет использовать функция.
Важно, что пользователь также может использовать словарь, но перед ним нужно ставить две звездочки (**):
Порядок вывода также не определен, потому что словарь не отсортирован.
Документирование функции
Команда docstring должна быть первой инструкцией после объявления функции. Ее потом можно будет извлекать или дополнять:
Методы, функции и атрибуты, связанные с объектами функции
Если поискать доступные для функции атрибуты, то в списке окажутся следующие методы (в Python все является объектом — даже функция):
И несколько скрытых методов, функций и атрибутов. Например, можно получить имя функции или модуля, в котором она определена:
Есть и другие. Вот те, которые не обсуждались:
Рекурсивные функции
Другой распространенный пример — определение последовательности Фибоначчи:
Важно, чтобы в ней было была конечная инструкция, иначе она никогда не закончится. Реализация вычисления факториала выше, например, не является надежной. Если указать отрицательное значение, функция будет вызывать себя бесконечно. Нужно написать так:
Важно!
Рекурсия позволяет писать простые и элегантные функции, но это не гарантирует эффективность и высокую скорость исполнения.
Глобальная переменная
Вот уже знакомый пример с глобальной переменной:
За редкими исключениями глобальные переменные лучше вообще не использовать.
Присвоение функции переменной
С существующей функцией func синтаксис максимально простой:
Переменным также можно присваивать встроенные функции. Таким образом позже есть возможность вызывать функцию другим именем. Такой подход называется непрямым вызовом функции.
Менять название переменной также разрешается:
В этом примере a1, a2 и func имеют один и тот же id. Они ссылаются на один объект.
Последний пример. Предположим, встроенная функция была переназначена:
Теперь к ней нельзя получить доступ, а это может стать проблемой. Чтобы вернуть ее обратно, нужно просто удалить переменную:
Анонимная функция: лямбда
С помощью type() можно проверить тип:
На практике эти функции редко используются. Это всего лишь элегантный способ записи, когда она содержит одну инструкцию.
Изменяемые аргументы по умолчанию
Вместо этого нужно использовать значение «не указано» и заменить на изменяемый объект по умолчанию:
Функции в Python — синтаксис, аргументы, вызов, выход
Функция — это фрагмент программного кода, который решает какую-либо задачу.
Его можно вызывать в любом месте основной программы. Функции помогают избегать дублирования кода при многократном его использовании. А также имеют ряд других преимуществ, описанных ниже.
Синтаксис
💁♀️ Простой пример: Вы торгуете мёдом, и после каждой продажи вам нужно печатать чек. В нём должно быть указано: название фирмы, дата продажи, список наименований проданных товаров, их количество, цены, общая сумма, а также сакраментальная фраза «Спасибо за покупку!».
Если не пользоваться функциями, всё придётся прописывать вручную. В простейшем случае программа будет выглядеть так:
print(«ООО Медовый Гексагон») print(«Мёд липовый», end=» «) print(1, end=»шт «) print(1250, end=»р») print(«\nCумма», 1250, end=»р») print(«\nСпасибо за покупку!»)
А теперь представьте, что произойдёт, когда вы раскрутитесь, и покупатели станут приходить один за другим. В таком случае, чеки надо будет выдавать очень быстро. Но что делать, если вдруг нагрянет ваш любимый клиент и купит 10 сортов мёда в разных количествах? Далеко не все в очереди согласятся ждать, пока вы посчитаете общую сумму и внесёте её в чек.
Хорошо, что данный процесс можно легко оптимизировать с использованием функций.
Встаёт резонный вопрос: где же обещанное упрощение и куда подевались товары? Как раз для этого, мы и будем описывать состав покупки не напрямую в функции, а в отдельном списке кортежей. Каждый кортеж состоит из трёх элементов: название товара, количество и цена.
# (название, количество, цена за штуку) honey_positions = [ («Мёд липовый», 3, 1250), («Мёд цветочный», 7, 1000), («Мёд гречишный», 6, 1300), («Донниковый мёд», 1, 1750), («Малиновый мёд», 10, 2000), ]
Теперь этот список передадим в функцию как аргумент, и самостоятельно считать больше не придётся.
Да, код стал более массивным. Однако теперь для печати чека вам не придётся самостоятельно вычислять итог. Достаточно лишь изменить количество и цену товаров в списке. Существенная экономия времени! Слава функциям!
Термины и определения
Ключевое слово def в начале функции сообщает интерпретатору о том, что следующий за ним код — есть её определение. Всё вместе — это объявление функции.
# объявим функцию my_function() def my_function(): # тело функции
Аргументы часто путают с параметрами:
Ключевая особенность функций — возможность возвращать значение
# она будет принимать два множителя, а возвращать их округленное # до целого числа произведение def int_multiple(a, b): product = a * b # возвращаем значение return int(product) print(int_multiple(341, 2.7)) > 920
☝️ Главная фишка возвращаемых значений в том, что их можно использовать в дальнейшем коде: присваивать переменным, совершать с ними разные операции и передавать как аргументы в другие функции.
# найдём квадратный корень из возврата функции int_multiple # во встроенную функцию sqrt() мы передали вызов int_multiple print(math.sqrt(int_multiple(44, 44))) > 44
Важность функций
Абстракция
Человек бежит, машина едет, корабль плывёт, а самолёт летит. Всё это — объекты реального мира, которые выполняют однотипные действия. В данном случае, они перемещаются во времени и пространстве. Мы можем абстрагироваться от их природы, и рассматривать эти объекты с точки зрения того, какое расстояние они преодолели, и сколько времени на это ушло.
Мы можем написать функцию, которая вычисляет скорость в каждом конкретном случае. Нам не важно, кто совершает движение: и для человека и для самолёта средняя скорость будет рассчитываться одинаково.
def calculate_speed(distance, time): return distance / time
Это простой пример и простая функция, но абстракции могут быть куда более сложными. И именно тогда раскрывается настоящая сила функций. Вместо того чтобы решать задачу для каждого конкретного случая, проще написать функцию, которая находит решение для целого ряда однотипных, в рамках применяемой абстракции, объектов. В случае сложных и длинных вычислений, это повлечёт за собой значительное сокращение объёмов кода, а значит и времени на его написание.
Возможность повторного использования
Функции были созданы ради возможности их многократного применения. Код без функций превратился бы в огромное нечитаемое полотно, на порядки превышающее по длине аналогичную программу с их использованием.
Например, при работе с массивами чисел, вам нужно часто их сортировать. Вместо того чтобы реализовать простой алгоритм сортировки (или использовать встроенную функцию), вам пришлось бы каждый раз перепечатывать тело этой или похожей функции:
Всего 10 таких сортировок, и привет, лишние 60 строк кода.
Модульность
Разбитие больших и сложных процессов на простые составляющие — важная часть, как кодинга, так и реальной жизни. В повседневности мы занимаемся этим неосознанно. Когда убираемся в квартире, мы пылесосим, моем полы и окна, очищаем поверхности от пыли и наводим блеск на всё блестящее. Всё это — составляющие одного большого процесса под названием «уборка», но каждую из них также можно разбить на более простые подпроцессы.
В программировании модульность строится на использовании функций. Для каждой подзадачи — своя функция. Такая компоновка в разы улучшает читабельность кода и уменьшает сложность его дальнейшей поддержки.
Допустим, мы работаем с базой данных. Нам нужна программа, которая считывает значения из базы, обрабатывает их, выводит результат на экран, а затем записывает его обратно в базу.
Без применения модульности получится сплошная последовательность инструкций:
Но если вынести каждую операцию в отдельную функцию, то текст главной программы получится маленьким и аккуратным.
Это и называется модульностью.
Пространство имен
Концепция пространства имён расширяет понятие модульности. Однако цель — не облегчить читаемость, а избежать конфликтов в названиях переменных.
💁♀️ Пример из жизни: в ВУЗе учатся два человека с совпадающими ФИО. Их нужно как-то различать. Если сделать пространствами имён группы этих студентов, то проблема будет решена. В рамках своей группы ФИО этих студентов будут уникальными.
Объявление и вызов функций
def hello(): print(‘Adele is cute’)
После того как мы это сделали, функцию можно вызвать в любой части программы, но ниже самого объявления.
# код выполняется последовательно, поэтому сейчас интерпретатор # не знает о существовании функции hello hello() def hello(): print(‘Adele is cute’) > NameError: name ‘hello’ is not defined
Поэтому стоит лишь поменять объявление и вызов местами, и всё заработает:
def hello(): print(‘Adele is cute’) hello() > Adele is cute
Область видимости функций
Рассмотрим подробнее области видимости:
Локальная (L)
Локальная область видимости находится внутри def :
def L(): # переменная i_am_local является локальной внутри L() i_am_local = 5
Область объемлющих функций (E)
def e(): x = 5 def inner_e(): nonlocal x x = x + 1 return x return inner_e() print(e()) > 6
Глобальная (G)
# G num = 42 def some_function(n): res = n + num return res print(some_function(1)) > 43
Аргументы
Позиционные
Вспомним, аргумент — это конкретное значение, которое передаётся в функцию. Аргументом может быть любой объект. Он может передаваться, как в литеральной форме, так и в виде переменной.
Значения в позиционных аргументах подставляются согласно позиции имён аргументов:
Именованные
Пусть есть функция, принимающая три аргумента, а затем выводящая их на экран. Python позволяет явно задавать соответствия между значениями и именами аргументов.
def print_trio(a, b, c): print(a, b, c) print_trio(c=4, b=5, a=6) > 6 5 4
При вызове соответствие будет определяться по именам, а не по позициям аргументов.
Необязательные параметры (параметры по умолчанию)
Python позволяет делать отдельные параметры функции необязательными. Если при вызове значение такого аргумента не передается, то ему будет присвоено значение по умолчанию.
def not_necessary_arg(x=’My’, y=’love’): print(x, y) # если не передавать в функцию никаких значений, она отработает со значениями по умолчанию not_necessary_arg() > My love # переданные значения заменяют собой значения по умолчанию not_necessary_arg(2, 1) > 2 1
Аргументы переменной длины (args, kwargs)
Когда заранее неизвестно, сколько конкретно аргументов будет передано в функцию, мы пользуемся аргументами переменной длины. Звёздочка «*» перед именем параметра сообщает интерпретатору о том, что количество позиционных аргументов будет переменным:
def infinity(*args): print(args) infinity(42, 12, ‘test’, [6, 5]) > (42, 12, ‘test’, [6, 5])
Переменная args составляет кортеж из переданных в функцию аргументов.
Функции в питоне могут также принимать и переменное количество именованных аргументов. В этом случае перед названием параметра ставится » ** «:
def named_infinity(**kwargs): print(kwargs) named_infinity(first=’nothing’, second=’else’, third=’matters’) >
Здесь kwargs уже заключает аргументы не в кортеж, а в словарь.
Передача по значению и по ссылке
В Python аргументы могут быть переданы, как по ссылке, так и по значению. Всё зависит от типа объекта.
Изменяемые объекты передаются в функцию по ссылке. Изменяемыми они называются потому что их содержимое можно менять, при этом ссылка на сам объект остается неизменной.
В Python изменяемые объекты это:
Будьте внимательны при передаче изменяемых объектов. Одна из частых проблем новичков.
💭 В функциональном программировании существует понятие «функциями с побочными эффектами» — когда функция в процессе своей работы изменяет значения глобальных переменных. По возможности, избегать таких функций.
Словарь в качестве аргументов (упаковка)
Передаваемые в функцию аргументы можно упаковать в словарь при помощи оператора «**»:
def big_dict(**arguments): print(arguments) big_dict(key=’value’) >
Возвращаемые значения (return)
Что можно возвращать
Функции в Python способны возвращать любой тип объекта.
Распаковка возвращаемых значений
☝️ Обратите внимание, что количество возвращаемых значение в кортеже должно совпадать с количеством переменных при распаковке. Иначе произойдет ошибка:
Пустая функция
Иногда разработчики оставляют реализацию на потом, и чтобы объявленная функция не генерировала ошибки из-за отсутствия тела, в качестве заглушки используется ключевое слово pass :
Чистые функции и побочные эффекты
Немного функционального программирования. Есть такие функции, которые при вызове меняют файлы и таблицы баз данных, отправляют данные на сервер или модифицируют глобальные переменные. Всё это — побочные эффекты.
У чистых функций побочных эффектов нет. Такие функции не изменяют глобальные переменные в ходе выполнения, не рассылают и не выводят на печать никакие данные, не касаются объектов, и так далее.
Чистые функции производят вычисления по заданным аргументам и возвращают зависящий только от них самих результат.
Lambda функции
lambda_test = lambda a, b: pow(a, b) print(lambda_test(2, 4)) > 16
Docstring
Документировать код — особое искусство. Оно существует параллельно с разработкой и сопоставимо с ней по важности. Поэтому нередко документации в программе больше, чем самого кода.
Когда над проектом работает большая команда, а может и не одна, да и еще и много лёт подряд, то значение и важность документации возрастают прямо пропорционально.
Аннотация типов
Python — язык с динамической типизацией. По этой причине вполне возможны ситуации, когда вопреки ожиданиям разработчика в функцию подаются, например, не целые числа, а, допустим, строки. Чтобы отслеживать подобные случаи и сильнее контролировать процесс выполнения программы, была изобретена аннотация типов.
С помощью аннотации типов мы указываем, что параметры в функции имеют строго определенный тип.
При этом интерпретатор считывает аннотации типов, но никак их не обрабатывает.
Функции vs процедуры — в чем отличие?
Для языка нет различий между функциями и процедурами. Но с точки зрения программиста — это разные сущности.
Отличие в том, что функции возвращают значение, а процедуры — нет. Отсюда вытекают и разные области их применения и смысл использования. Скажем, производить некие вычисления в процедуре бессмысленно.
def proc(i, j): pow(i, j) proc(1, 200)
def func(i, j): return pow(i, j) print(func(3, 2)) > 9
И наоборот, оформлять набор инструкций, выполняющий некую обработку, в виде функции также лишено смысла:
def print_low_word(word): print(word.lower()) return 0 s = ‘GOOD’ print_low_word(s) > good
Возвращаемое значение не представляет собой никакой ценности, поэтому print_low_word(s) лучше оформить, как процедуру.
Время выполнения функции
Чтобы оценить время выполнения функции, можно поместить её вызов внутрь следующего кода:
Вложенные функции и рекурсия
Функции, которые объявляются и вызываются внутри других функций, называются вложенными.
def outerFunc(): def firstInner(): print(‘This is first inner function’) def secondInner(): print(‘This is second inner function’) firstInner() secondInner() outerFunc() > This is first inner function > This is second inner function
Рекурсия является частным случаем вложенной функции. Это функция, которая вызывает саму себя.