Динамически вызывать функцию, перечислять функции с определенной частью их имени и т. Д.

Эффективный рефакторинг существующего кода - это искусство. Если вы когда-либо унаследовали чужой код или даже через некоторое время оглянулись на свой собственный, вы знаете, насколько сложным может быть этот процесс. Часто функции создаются и ошибки исправляются в сжатые сроки, что может привести к небрежному и неэффективному коду.

Это совершенно нормально. Вам разрешено время от времени использовать некоторые неверные коды. Крайне важно, чтобы вы могли идентифицировать плохой код и всегда возвращаться, чтобы исправить его.

В этой статье мы рассмотрим некоторые небольшие сегменты кода Python, которые упрощают рефакторинг для получения более чистого кода. В большинстве случаев они используют простые стандартные библиотечные функции и не скрывают намерений, стоящих за чрезвычайно сложными алгоритмами или плотными однострочниками. При рефакторинге кода вам нужен хороший баланс между краткостью и удобочитаемостью. Давайте посмотрим на несколько примеров.

Динамический вызов функций

def call_func(name):
    klass = MyClass()
    return getattr(klass, name)()
call_func('function_name')

Если вы встретите участки кода, в которых вы не обязательно узнаете имя функции, которую хотите вызвать, на потом, это ответ. Используя встроенную функцию Python getattr, вы можете вызывать функцию по имени, используя строку. Это отлично подходит для уменьшения количества тесно связанного кода и улучшения расширяемости. Если вы уменьшите количество жестко запрограммированных имен функций и обеспечите большую гибкость, то есть большая вероятность, что ваш код будет меньше ломаться.

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

Подробнее о функции getattr вы можете прочитать в документации.

Использование деструктурирующего присваивания

var_one, var_two = [1, 2]

Вы, наверное, слышали об этой концепции миллион раз раньше, но стоит повторить еще раз: используйте деструктурирующее присваивание там, где это возможно, чтобы отображать значения и более четко передавать намерения. Когда вы получаете доступ к списку или массиву по значениям индекса, вы непреднамеренно скрываете свое намерение. Обращение к значениям по четкому имени переменной, присвоенному посредством деструктурирующего присваивания, упрощает чтение вашего кода.

В приведенном ниже примере у нас есть некоторые данные о человеке в списке. Эти данные включают основную информацию, такую ​​как имя, возраст и профессия человека. Давайте предположим для этого примера, что это часть ответа, который мы получили на веб-запрос, и что нам нужно будет проанализировать несколько ответов точно так же. Если бы мы хотели получить имя человека, мы могли бы просто получить к нему доступ по значению индекса:

person = ['Bill', 26, 'Programmer']
print(person[0])

Это небрежно и скрывает намерение, если вы когда-либо упоминали его более одного раза. Ссылки на person[0] повсюду в вашем коде затруднят интерпретацию того, какое значение должно быть. Для этого есть очень простое исправление, которое не скрывает ваши намерения и делает ваш код более читабельным. Просто назначьте это переменной и, если возможно, также деструктурируйте весь список на переменные с соответствующими именами. Давайте посмотрим на пример применения этого на практике:

person = ['Bill', 26, 'Programmer']
name, age, profession = person
print(name)

Теперь, если вам понадобится ссылаться на имя позже, вы сделаете это через именованную переменную, а не через значение индекса. Это хорошо работает для данных ответа, структура которых вам наверняка известна. Пока информация о человеке всегда находится в одном и том же порядке, вы можете деструктурировать и назначать все снова и снова, чтобы избежать использования значений индекса.

Список функций с определенным шаблоном

В этом примере предположим, что у нас есть класс, содержащий большое количество функций. Некоторые из этих функций служат для специальных целей и должны быть динамически доступны. Мы хотим, чтобы любая специальная функция, которую мы добавляем в этот класс, немедленно обрабатывалась остальной частью нашего кода, чтобы она сразу же начала работать. Один из способов добиться этого - просто использовать dir в содержащем классе.

Если вы передадите класс dir, он перечислит все атрибуты для этого объекта. Сюда входят названия его функций. Путем фильтрации префикса, суффикса или ключевого слова, включенного в имя строки, мы можем сгруппировать функции этого класса по различным категориям. Это позволяет нам «подбирать» новые функции, имена которых включают ключевое слово, и либо вызывать их все, либо выполнять с ними другую логику позже в коде.

Узнайте больше о встроенной функции dir в документации.

Передача нескольких аргументов функций как Dict

Есть большая вероятность, что вы уже сталкивались с чем-то подобным раньше при рефакторинге кода:

Хотя определенные форматы этого стиля отступа могут быть технически разрешены в PEP8, мне лично не нравится смотреть на аргументы функций с сильным отступом. Вы вынуждены переводить взгляд полностью вправо, некоторые IDE плохо справляются с этим отступом, и использование этого стиля препятствует будущей расширяемости.

Один из вариантов улучшения читабельности и повторного использования при большом количестве аргументов функции - передать их в форме dict. Вот пример этого:

Использование оператора ** в dict при передаче dict функции позволяет успешно распаковать все аргументы (вместо случайной передачи самого объекта dict). Хотя этот метод добавляет еще одну переменную и отдельную структуру данных, он улучшает общую читаемость при большом количестве аргументов. Перемещение вызова функции или ссылка на несколько вызовов одной и той же функции с одинаковыми (или слегка измененными) аргументами теперь становится бесконечно проще и требует меньших общих изменений.

Заключение

Спасибо за чтение! Я надеюсь, что эти фрагменты кода помогут повысить вашу эффективность в следующий раз, когда вы обнаружите, что разбираете код Python на части.