8 быстрых советов по рефакторингу Python для более чистого и более Pythonic-кода.
1. Объединить добавление в объявление списка
Начнем с простого. Вместо объявления пустого списка и последующего добавления к нему, просто инициализируйте список напрямую со всеми элементами. Это сокращает код и делает намерение более явным. Это также немного более производительно, так как позволяет избежать вызовов функции append () .
players = [] players.append("Patrick") players.append("Max") players.append("Jessi") # -> refactor players = ["Patrick", "Max", "Jessi"]
То же самое относится и к заполнению других типов коллекций, таких как наборы и словари.
2. Используйте items() для прямой распаковки значений словаря
При переборе словаря, когда вам нужны и ключ, и значение, не обращайтесь к значениям вручную. Вместо этого выполните итерацию по словарю.items() , который одновременно дает вам и ключи, и значения.
Это избавляет нас от строки, которую мы использовали для назначения игрокам , и код теперь читается более естественно, с меньшим дублированием.
teams_by_color = {"blue": ["Patrick", "Jessi"]} for team_color in teams_by_color: players = teams_by_color[team_color] if is_winning(team_color): advance_level(players) # -> refactor for team_color, players in teams_by_color.items(): if is_winning(team_color): advance_level(players)
3. Замените диапазон (длина) перечислением
Если нам нужно выполнить итерацию по списку и отслеживать как индекс, так и текущий элемент, используйте встроенную enumerate()
функцию вместо range(len)
. Это возвращает как текущий индекс, так и текущий элемент в виде кортежа. Таким образом, мы можем напрямую проверить значение здесь, а также получить доступ к элементу с индексом.
for i in range(len(players)): print(i, players[i]) # -> refactor for i, player in enumerate(players): print(i, player)
Enumerate также поставляется с необязательным начальным аргументом. Если вы используете его, счетчик начинается с этого значения. Но имейте в виду, что элементы по-прежнему начинаются с самого первого.
for i, player in enumerate(players, start=1):
print(i, player)
4. Замените ручной счетчик циклов вызовом перечисления
Это очень похоже на то, что было раньше. Иногда я вижу код, в котором итерация выполняется непосредственно над элементами — что само по себе неплохо, — но тогда требуется счетчик, и он увеличивается вручную внутри цикла. Опять же, здесь вы можете просто использовать функцию перечисления . Это проще и к тому же быстрее.
i = 0 for player in players: print(i, player) i += 1 # -> refactor for i, player in enumerate(players): print(i, player)
4.1 Не обновлять счетчик вручную
Если вам просто нужно подсчитать количество элементов, также не повторяйте цикл и вручную подсчитывайте все элементы. Вместо этого просто используйте len()
функцию, чтобы получить количество элементов в списке.
num_players = 0 for player in players: num_players += 1 # -> refactor num_players = len(players)
5. Упростите условное выражение до оператора return
Когда мы достигаем конца метода и хотим вернуть True или False , обычно это делается так. Если условие истинно, мы возвращаем True. В противном случае мы возвращаем False в конце. Однако лучше просто вернуть результат напрямую:
def function():
if isinstance(a, b) or issubclass(b, a):
return True
return False
# -> refactor
def function():
return isinstance(a, b) or issubclass(b, a)
Одна вещь, о которой мы должны знать, это то, что это можно сделать только в том случае, если выражение оценивается как логическое. Обе функции isinstance() и issubclass() возвращают логическое значение, так что это нормально. Но в следующем примере первое выражение pythonistas — это список, а не логическое значение.
Если pythonistas является действительным непустым списком, это вернет список вместо ожидаемого логического значения, а затем потенциально может быть ошибкой в вашем приложении. Итак, чтобы убедиться, что мы возвращаем здесь логическое значение, мы можем обернуть возврат в вызов функции bool() .
def any_pythonistas():
pythonistas = [coder for coder in coders if is_good_in_python(coder)]
if pythonistas or self.is_pythonista():
return True
return False
# -> refactor
def any_hats():
pythonistas = [coder for coder in coders if is_good_in_python(coder)]
return bool(pythonistas or self.is_pythonista())
6. Объединить повторяющиеся блоки в условном
Мы всегда должны искать возможности для удаления повторяющегося кода. Лучше всего это делать там, где есть несколько одинаковых блоков внутри цепочки if…elif .
В этом примере и if, и elif приводят к одной и той же выполняемой функции. Таким образом, мы можем объединить первые два блока, используя или , чтобы удалить повторяющийся вызов функции. Теперь, если нам нужно изменить строку process_standard_payment(), мы можем сделать это в одном месте вместо двух.
def process_payment(payment, currency): if currency == "USD": process_standard_payment(payment) elif currency == "EUR": process_standard_payment(payment) else: process_international_payment(payment) # -> refactor def process_payment(payment, currency): if currency == "USD" or currency == "EUR": process_standard_payment(payment) else: process_international_payment(payment)
7. Замените несколько сравнений одной и той же переменной оператором in
Мы можем реорганизовать предыдущий код еще на один шаг вперед. Поскольку мы неоднократно проверяем одну и ту же переменную на несколько значений, мы можем сократить это, используя оператор in. Если значение валюты находится в определенном списке, мы выполняем специальное действие.
def process_payment(payment, currency):
if currency == "USD" or currency == "EUR":
process_standard_payment(payment)
else:
process_international_payment(payment)
# -> refactor
def process_payment(payment, currency):
if currency in ["USD", "EUR"]:
process_standard_payment(payment)
else:
process_international_payment(payment)
И чтобы улучшить это еще раз, мы должны использовать множество здесь . Поиск значений в множестве выполняется быстрее, и нам в любом случае нужны здесь уникальные элементы, поэтому множество — лучший выбор.
# -> refactor
def process_payment(payment, currency):
if currency in {"USD", "EUR"}:
process_standard_payment(payment)
else:
process_international_payment(payment)
8. Замените yield внутри цикла for на yield from
Это продвинутый совет, если вы уже знакомы с генераторами. Одна небольшая хитрость, которую часто упускают из виду, заключается в том, что ключевое слово yield в Python имеет соответствующий yield from для итерируемых объектов.
Если у вас есть итерируемый список, вместо того, чтобы говорить for item in iterable: yield item
, вы можете просто сказать yield from iterable
. Это короче и устраняет ручной цикл по итерации, что также может привести к повышению производительности.
def get_content(entry): for block in entry.get_blocks(): yield block # -> refactor def get_content(entry): yield from entry.get_blocks()
Бонус: бесплатное расширение для рефакторинга VS Code/PyCharm:
Существует расширение для VS Code и PyCharm, которое поможет вам идентифицировать эти шаблоны рефакторинга. Это расширение Sourcery. Sourcery — это бесплатное расширение, которое вы можете легко установить и которое затем даст вам полезные советы по рефакторингу. Вы можете установить и протестировать его здесь: Sourcery.ai