pydonts
Общие рекомендации
Следуй PEP-8. Для автоматической проверки и исправления некоторых ошибок можно использовать утилиты pep8 и autopep8. К сожалению, они проверяют не всё (например, конвенции именования не проверяются). В качестве альтернативы можно использовать статический анализатор flake8, который помимо стиля проверит наличие некоторых простых ошибок. Плагин для него, проверяющий именование, называется pep8-naming.
Располагай импортируемые модули в начале файла.
Располагай импортируемые модули в лексикографическом порядке.
# Плохо import gzip import sys from collections import defaultdict import io from contextlib import contextmanager import functools from urllib.request import urlopen # Лучше import functools import gzip import io import sys from collections import defaultdict from contextlib import contextmanager from urllib.request import urlopen
Используй операторы
is
иis not
только для сравнение с синглтонами, например,None
. Исключение: булевые значенияTrue
иFalse
.Помни и применяй falsy/truthy семантику. Falsy значения ---
None
,False
, нули0
,0.0
,0j
, пустые строки и байты и все пустые коллекции. Все остальные значения truthy.# Плохо if acc == []: # ... # Плохо if len(acc) > 0: # ... # Лучше if not acc: # ... # Допустимо if acc == 0: # ...
Не называй переменные именами коллекций. Почти всегда можно подобрать более уместное имя.
Не копируй без необходимости.
# Плохо set([x**2 for x in range(42)]) for x in list(sorted(xs)): # ... # Лучше {x**2 for x in range(42)} for x in sorted(xs): # ...
Не используй
dict.get
и коллекциюdict.keys
для проверки наличия ключа в словаре:# Плохо if key in g.keys(): # ... if not g.get(key, False): # ... # Лучше if key in g: # ... if key not in g: # ...
Используй литералы для создания пустых коллекций. Исключение:
set
, литералов пустого множества в Python нет.# Плохо dict(), list(), tuple() # Лучше {}, [], ()
Структура кода
Не эмулируй оператор
for
, Python --- это не Scala.# Плохо i = 0 while i < n: ... i += 1 # Лучше for i in range(n): ...
Предпочитай итерацию по объекту циклам со счётчиком. Ошибка на 1 в индексе --- это классика. Если же индекс требуется, помни про
enumerate
.# Плохо for i in range(len(xs)) : x = xs[i] # Лучше for x in xs: ... # Или for i, x in enumerate(xs): ...
# Плохо for i in range(min(len(xs), len(ys))): f(xs[i], ys[i]) # Лучше for x, y in zip(xs, ys): f(x, y)
Не используй
dict.keys
для итерации по словарю.# Плохо for key in dict.keys(): ... # Лучше for key in dict: ...
Не используй методы
file.readline
иfile.readlines
для итерации по файлу.# Плохо while True: line = file.readline() ... for line in file.readlines(): ... # Лучше for line in file: ...
Не пиши бессмысленных операторов
if
и тернарных операторов.# Плохо if condition: return True else return False # Лучше return condition
# Плохо if condition: return False else return True # Лучше return not condition
# Плохо xs = [x for x in xs if predicate] return True if xs else False # Лучше xs = [x for x in xs if predicate] return bool(xs) # Ещё лучше return any(map(predicate, xs))
Функции
Избегай изменяемых значений по умолчанию.
Не злоупотребляй функциональными идиомами. Часто генератор списка, множества или словаря понятнее комбинации функций
map
,filter
иzip
.# Плохо list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 1, range(10)))) # Лучше [x ** 2 for x in range(10) if x % 2 == 1]
Не злоупотребляй генераторами коллекций. Часто обычный цикл
for
понятней вложенного генератора.Не сворачивай функции с эффектами. Первый аргумент
functools.reduce
не должен изменять состояние имён во внешних областях видимости или значение аккумулятора.# Плохо funtools.reduce(lambda acc, s: acc.update(s), sets, # Лучше acc = set() for set in sets: acc.update(set)
Избегай бессмысленных анонимных функций.
# Плохо map(lambda x: frobnicate(x), xs) # Лучше map(frobnicate, xs)
# Плохо collections.defaultdict(lambda: []) # Лучше collections.defaultdict(list)
Декораторы
- Всегда используй
functools.wraps
илиfunctools.update_wrapper
при написании декоратора.
Строки
Используй методы
str.startswith
иstr.endswith
.# Плохо s[:len(p)] == p s.find(p) == len(s) - len(p) # Лучше s.startswith(p) s.endswith(p)
Используй форматирование строк вместо явных вызовов
str
и конкатенации.# Плохо "(+ " + str(expr1) + " " + str(expr2) + ")" # Лучше "(+ {} {})".format(expr1, expr2)
Исключение: приведение к строке одного аргумента.
# Плохо "{}".format(value) # Лучше str(value)
Не усложняй шаблон форматирования без необходимости.
# Плохо "(+ {0} {1})" "(+ {expr1} {expr2})" # Лучше "(+ {} {})"
Помни, что метод
str.format
преобразует аргументы в строку.# Плохо "(+ {} {})".format(str(expr1), str(expr2)) # Лучше "(+ {} {})".format(expr1, expr2)
Классы
Используй
collections.namedtuple
для классов с фиксированным набором неизменяемых полей.# Плохо class Point: def __init__(self, x, y): self.x = x self.y = y # Лучше Point = namedtuple("Point", ["x", "y"])
Не вызывай "магические методы" напрямую, если для них есть функция или оператор.
# Плохо expr.__str__() expr.__add__(other) # Лучше str(expr) expr + other
Не используй
type
для проверки того, что объект --- экземпляр некоторого класса. Для этого больше подходит функцияisinstance
.# Плохо type(instance) == Point type(instance) is Point # Лучше isinstance(instance, Point)
Исключения
Минимизируй размер блоков
try
иwith
.Чтобы поймать любое исключение используй
except Exception
, а неexcept BaseException
или простоexcept
.Указывай наиболее специфичный тип исключения в блоке
except
.# Плохо try: mapping[key] except Exception: ... # Лучше try: mapping[key] except KeyError: ...
Наследуй собственные исключения от
Exception
, а не отBaseException
.Используй менеджеры контекста вместо
try-finally
.# Плохо handle = open("path/to/file") try: do_something(handle) finally: handle.close() # Лучше with open("path/to/file") as handle: do_something(handle)