Сообщить об ошибке.

Рекомендации по программированию

Придерживайтесь следующих рекомендаций по программированию

  • Код должен быть написан так, чтобы не зависеть от разных реализаций языка (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:, кроме двух случаев:

    1. Если обработчик выводит пользователю всё о случившейся ошибке; по крайней мере, пользователь будет знать, что произошла ошибка.
    2. Если нужно выполнить некоторый код после перехвата исключения, а потом вновь "бросить" его для обработки где-то в другом месте. Обычно же лучше пользоваться конструкцией 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: