Код должен быть написан так, чтобы не зависеть от разных реализаций языка (PyPy, Jython, IronPython, Pyrex, Psyco и т. д.).
Например, не полагайтесь на реализацию CPython конкатенации строк для операторов в форме a += b
или a = a + b
. В чувствительных к производительности частях кода следует использовать форму ''str.join()
. Это будет гарантировать, что конкатенация будет выполняться в линейное время в разных реализациях.
Сравнения с None
должны выполняться с использованием операторов is
или is not
, а не с помощью операторов сравнения. Кроме того, не пишите if x
, если имеете в виду if x is not None
, если, к примеру, при тестировании такая переменная может принять значение другого типа, отличного от None
, но при приведении типов может получиться False
!
Используйте is not
оператор вместо not ... is
. Хотя оба выражения функционально идентичны, первое является более читаемым и предпочтительным.
# Правильно if foo is not None: # Неправильно if not foo is None:
При реализации методов сравнения, лучше всего реализовать все 6 операций сравнения (__eq__
, __ne__
, __lt__
, __le__
, __gt__
, __ge__
), вместо того, чтобы полагаться на чужой код, чтобы реализовывать только определенные операции сравнения.
Для минимизации усилий в ходе реализации операций сравнения можно воспользоваться декоратором functools.total_ordering() для создания отсутствующих методов сравнения.
PEP 207 указывает, что интерпретатор может поменять y > х
на х < y
, y >= х
на х <= y
, и так же может менять местами аргументы х == y
и х != y
. Гарантируется, что операции sort()
и min()
используют оператор <
, а max()
использует оператор >
. Однако, лучше всего осуществить все шесть операций, чтобы не возникало путаницы в других местах.
Всегда используйте выражение def
, а не присваивание лямбда-выражений к имени.
# Правильно: def f(x): return 2*x # Неправильно: f = lambda x: 2*x
Первая запись означает, что имя результирующего объекта функции является конкретно f
вместо универсального <lambda>
. Это более полезно для трассировок и строковых представлений в целом. Использование оператора присваивания устраняет единственное преимущество, которое лямбда-выражение может предложить по сравнению с явным оператором 'def' (т. е. что оно может быть встроено в более крупное выражение)
Наследуйте свой класс исключения от Exception, а не от BaseException. Прямое наследование от BaseException зарезервировано для исключений, которые не следует перехватывать.
Создавайте иерархии исключений на основе различий, которые могут понадобиться коду, перехватывающему исключения, а не мест, где возникают исключения. Цель ответить на вопрос "Что пошло не так?" программно, а не просто заявляя, что «возникла проблема»
Здесь применяются соглашения об именах классов, хотя вы должны добавить суффикс Error
к классам исключений, если исключение является ошибкой. Исключения без ошибок, которые используются для нелокального управления потоком или других форм сигнализации, не нуждаются в специальном суффиксе.
Используйте цепочки исключений соответствующим образом. В Python 3, raise X from Y
следует использовать для указания явной замены без потери отладочной информации.
Когда намеренно заменяется исключение (использование raise X
в Python 2 или raise X from None
в Python 3.3+), проследите, чтобы соответствующая информация передалась в новое исключение (такие, как сохранение имени атрибута при преобразовании KeyError
в AttributeError
или вложение текста исходного исключения в новом).
При возбуждении исключения в Python 2 используйте raise ValueError('message')
вместо более старой формы, raise ValueError, 'message'
.
Старая форма записи запрещена в python 3.
Такое использование предпочтительнее, потому что из-за скобок не нужно использовать символы для продолжения перенесенных строк, если эти строки длинные или если используется форматирование.
При отлове ошибках упоминайте о конкретных исключениях, когда это возможно, вместо использования простого выражения except
:
try: import platform_specific_module except ImportError: platform_specific_module = None
Голое исключение except
: также будет ловить и SystemExit
, и KeyboardInterrupt
, что делает его более трудным , чтобы прервать программу с помощью Control-C, и может замаскировать другие проблемы. Если вы действительно собираетесь перехватить все исключения, пишите except Exception:
.
Хорошим правилом является ограничение использования except:
, кроме двух случаев:
try...finally
.При связывании перехваченных исключений с именем, предпочитайте явный синтаксис привязки имени:
try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
Это единственный синтаксис, поддерживающийся в Python 3, который позволяет избежать проблем неоднозначности, связанных с более старым синтаксисом на основе запятой.
При отлове ошибок операционной системы, предпочитайте использовать явную иерархию исключений, введенную в Python 3.3, вместо анализа значений errno.
Постарайтесь заключать в каждую конструкцию try...except
минимум кода, чтобы легче отлавливать ошибки. Опять же, это позволяет избежать замаскированных ошибок.
# Правильно: try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
# Неправильно: try: # Здесь много действий! return handle_value(collection[key]) except KeyError: # Также будет ловить KeyError, поднятую handle_value() return key_not_found(key)
Если ресурс является локальным для определенного раздела кода, используйте оператор with
, чтобы обеспечить его быструю и надежную очистку после использования. Заявление try/finally
также приемлемо.
Менеджеры контекста должны вызываться с помощью отдельных функций или методов всякий раз, когда они делают что-то иное, чем получение и освобождение ресурсов.
# Правильно: with conn.begin_transaction(): do_stuff_in_transaction(conn)
# Неправильно: with conn: do_stuff_in_transaction(conn)
Последний пример не дает никакой информации, указывающей на то, что __enter__
и __exit__
делают что-то кроме закрытия соединения после транзакции. Быть явным важно в данном случае.
Будьте последовательны с операторами return
. Либо все операторы return
в функции должны возвращать значение, либо ни один из них не должен. Если какой-либо оператор return
возвращает значение, любые операторы return
, для которых не возвращено значение, должны явно указать это как return None
, и в конце функции должен присутствовать явный оператор return
(если он достижим).
# Правильно: def foo(x): if x >= 0: return math.sqrt(x) else: return None def bar(x): if x < 0: return None return math.sqrt(x)
# Неправильно: def foo(x): if x >= 0: return math.sqrt(x) def bar(x): if x < 0: return return math.sqrt(x)
Используйте строковые методы вместо модуля string, они всегда быстрее и имеют тот же API для unicode-строк. Можно отказаться от этого правила, если необходима совместимость с версиями python младше 2.0.
В Python 3 остались только строковые методы.
Пользуйтесь ''.startswith()
и ''.endswith()
вместо обработки срезов строк для проверки суффиксов или префиксов.
startswith()
и endswith()
выглядят чище и порождают меньше ошибок.
# Правильно: if foo.startswith('bar'):
# Неправильно: if foo[:3] == 'bar':
Сравнение типов объектов нужно делать с помощью isinstance()
, а не прямым сравнением типов:
# Правильно: if isinstance(obj, int):
# Неправильно: if type(obj) is type(1):
Когда вы проверяете, является ли объект строкой, обратите внимание на то, что строка может быть unicode-строкой. В python 2 у str
и unicode
есть общий базовый класс, поэтому вы можете написать:
if isinstance(obj, basestring):
Отметим, что в Python 3, unicode
и basestring
больше не существуют (есть только str
) и bytes
больше не является своего рода строкой (это последовательность целых чисел).
Для последовательностей (строк, списков, кортежей) используйте тот факт, что пустая последовательность есть False
:
# Правильно: if not seq: if seq:
# Неправильно: if len(seq) if not len(seq)
Не пользуйтесь строковыми константами, которые имеют важные пробелы в конце - они невидимы, а многие редакторы (а теперь и reindent.py) обрезают их.
Не сравнивайте логические типы с True
и False
с помощью ==
:
# Правильно: if greeting:
# Неправильно: if greeting == True:
# Совсем неправильно: if greeting is True: