Изучаем PEP
8 по частям. Часть 7, Programming
Recommendations. Практически крайняя.
Тут говорится
о том, что:
Не надо писать
такой код, который будет трудно запустить
под другими Python машинами (PyPy, Jython, …).
Например, не
надейтесь на CPython-скую эффективность
реализации склеивания строк в форме a
+= b или a = a + b. Оптимизация этой операции
хрупка даже в CPython (работает только для
некоторых типов) и ее вовсе нет в машинах
не использующих подсчет ссылок. Более
правильным, с точки зрения производительности,
будет использование формы ''.join(). Так
можно гарантировать линейное время
выполнения операции.
Сравнение с
синглтонами вроде None следует всегда
делать в форме «x is None» или «x is not None», и
никогда через оператор сравнения.
Также,
остерегайтесь писать «if x» когда в
действительности вы подразумеваете
«if x is not None», например, когда проверяете
переменную по умолчанию установленную
в None. Можете натолкнуться на ситуацию,
когда некий тип данных (вроде контейнера)
может выдать False в контексте булевых
операций.
Используйте
«is not» оператор вместо «not … is». Хотя
функционально оба варианта идентичны,
первое более читабельно.
if foo is not None:
При реализации операций упорядочивания
с многовариантными
сравнениями, лучше написать все шесть
магических метода (__eq__, __ne__, __lt__, __le__,
__gt__, __ge__) нежели полагаться на то, что
другой код воспользуется только
некоторыми из них.
Чтобы не
перенапрягаться при этом, можете
воспользоваться декоратором
functools.total_ordering() для генерации недостающих
методов.
PEP
207 показывает что в Python предполагаются
правила рефлексивности. Так, интерпретатор
может заменить y > x на x < y и т. д.
Операции sort() и min() гарантируют использование
оператора < и функция max() использует
оператор >. Однако, лучше реализовать
все шесть и не опасаться возможных
неприятностей использования вашего
кода в других контекстах.
Всегда
используйте выражение «def ...» вместо
присваивания лямбда выражения
идентификатору
def f(x): return 2*xf = lambda x: 2*x
Первая форма означает, что имя получившегося
объекта (функции) есть «f» вместо общего
«<lambda>». Это полезнее при отладке и
выводе на печать. Использование
присваивания уничтожает единственную
выгоду от использования лямбды —
анонимность, приводящую к возможности
использования лямбды в составном
выражении.
Наследуйте
свой класс исключений от класса Exception
а не от BaseException. Прямое наследование от
BaseException зарезервировано для исключений,
которые не надо перехватывать.
Проектируйте
иерархию классов исключений основываясь
на том, что нужно коду, поймавшему
исключение, в противовес месту, где
случилось исключение. Нацеливайтесь
ответить на вопрос «что пошло не так?»,
нежели декларировать «случилась
неприятность». См. PEP
3151 как на пример.
Тут применимы
правила именования классов, только
добавляйте суффикс «Error» для тех
исключений, что отлавливают ошибки.
Исключения сигнализирующие о чем-то
другом нуждаются в других суффиксах.
Используйте
цепочки исключений (exception chaining) разумно.
В Python 3 выражение «raise X from Y» следует
использовать для указания явной замены
без потери оригинального стека
(traceback).
В случае
умышленной замены внутреннего исключения
(используя «raise X» в Python 2 или «raise X from
None» в Python 3.3+), потрудитесь обеспечить
передачу важной информации в новое
исключение (сохраняя имя атрибута,
превращая KeyError в AttributeError, или текст
сообщения).
В Python 2 используйте
форму вызова «raise ValueError('message')» вместо
«raise ValueError, 'message'».
Второй способ
устарел и не работает в Python 3.
Вариант со
скобочками также означает, что легче
будет писать длинные аргументы с
переносами строк.
При отлове
исключений, нацеливайтесь на конкретные
варианты вместо отлова более общих
try: import platform_specific_module except ImportError: platform_specific_module = Noneexcept:platform_specific_module = None
Выражение «except:» поймает и SystemExit и
KeyboardInterrupt и еще бог знает что, затрудняя
диагностику и мешая интерактивности
(обработка Ctrl-C). Если хотите отлавливать
все программные ошибки, используйте
«except Exception:», потому как чистый «except:»
равносилен «except BaseException:».
Хорошая эмпирика
такова, использовать «except:» только в
двух случаях:
1. Если обработчик
исключения печатает или логирует стек
ошибки, так хотя бы пользователь увидит,
что за ошибка была.
2. Если нужно
в коде проделать работу по чистке
чего-либо. Но затем надо выкинуть
исключение обратно. В этом случае может
быть лучше использовать «try... finally».
При связывании
отловленного исключения с именем,
делайте это явным образом, в синтаксисе,
доступном еще в Python 2.6
try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
Только этот синтаксис поддерживается
в Python 3, в нем избегается неоднозначность
присущая старому синтаксису, который
с запятыми.
При отлове
ошибок операционной системы используйте
иерархию исключений, представленную в
Python 3.3 вместо изучения значений errno.
В дополнение,
для всех выражений try/except сводите
содержимое блока try к минимуму, это
помогает локализовать ошибки
try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)try:return handle_value(collection[key])except KeyError:return key_not_found(key)
Используя
ресурс, локальный конкретному участку
кода, используйте выражение «with» для
надежной и быстрой зачистки после его
использования. Выражение try/finally тоже
неплохо.
Менеджеры
контекста следует вызывать через разные
методы/функции, когда они выполняют
что-то отличное от захвата и освобождения
ресурсов. К примеру:
with conn.begin_transaction(): do_stuff_in_transaction(conn)with conn:do_stuff_in_transaction(conn)
во втором примере нет информации для
менеджера контекста о том, что надо
отработать транзакцию (коммит сделать,
в частности), только открытие/закрытие
коннекта. Важно явно выразить происходящее.
Используйте
методы строкового типа а не функции
строкового модуля.
Методы строки
всегда быстрее и у них тот-же API, что и у
юникодных строк. Но это правило неприменимо
если нужна обратная совместимость с
Python дряхлее 2.0.
Используйте
''.startswith() и ''.endswith() вместо применения
слайсов, чтобы проверить суффиксы и
префиксы.
Эти функции
чище и менее подвержены ошибкам
if foo.startswith('bar'):if foo[:3] == 'bar':
Сравнение
типов объектов следует делать через
isinstance()
if isinstance(obj, int):if type(obj) is type(1):
Проверяя, что некий объект это строка,
помните, что это может быть юникодная
строка. В Python 2 str и unicode происходят от
одного базового класса basestring, поэтому
проверка может быть
if isinstance(obj, basestring):
Но, в Python 3 уже нет unicode и basestring, только
str и bytes, причем bytes это и не строка вовсе
а последовательность целых чисел.
Для
последовательностей (строки, списки,
кортежи) используйте тот факт, что пустая
последовательность дает False
if seq:if len(seq):
Не записывайте
строковые литералы, для которых важно
иметь пробелы в конце. Такие оконечные
пробелы не различимы взглядом и некоторые
инструментальные средства их режут.
Не сравнивайте
булевы значения с True/False используя «==»
if greeting:if greeting == True:if greeting is True:
Стандартная
библиотека Python не будет использовать
аннотации функций, так как это приведет
к преждевременному вложению сил в
конкретный стиль аннотаций. Мы лучше
подождем, пока сообщество походит по
граблям и не выработает приемлемые
варианты.
Рекомендовано
использовать ассоциированный декоратор
при экспериментах с аннотациями, для
индикации способа интерпретации
аннотаций.
Ранние попытки
использовать аннотации функций выявили
некоторую несогласованность. К примеру:
* [str] не дает
ответа — это список строк или значение
суть строка либо None.
* Нотация
open(file:(str, bytes)) была использована в случае,
когда значение может быть bytes или str
вместо кортежа из двух значений.
* Нотация
seek(whence:int) показывает одновременно
овер-спецификацию и недо-спецификацию:
int слишком ограничивающ (подойдет все,
что угодно, обладающее __index__), при этом
он недостаточно ограничивающ (допустимы
только значения 0, 1, 2). Также, аннотация
write(b: bytes) слишком ограничивает, подошло
бы что угодно с поддержкой протокола
buffer.
* Аннотации
вроде read1(n: int=None) противоречат сами себе,
ибо None это не int. Аннотации вроде
source_path(self, fullname:str) -> object сеют сомнения
в типе возвращаемого значения.
* В дополнение
к указанному, были аннотации несогласованные
по использованию конкретных/абстрактных
типов: int versus Integral и set/frozenset versus
MutableSet/Set.
* Некоторые
аннотации в абстрактных базовых классах
имели некорректные спецификации.
Например, операции set-to-set требуют, чтобы
other был другим экземпляром Set нежели
просто Iterable.
* Более того,
аннотации становятся частью спецификации,
но не были протестированы.
* В большинстве
случаев, докстринги уже включают
спецификации типов и делают это с большей
ясностью, чем аннотации функций. В других
случаях, докстринги были улучшены после
удаления аннотаций.
* Рассмотренные
аннотации функций были слишком кустарными
и несогласованными, чтобы работать в
составе системы автопроверки типов или
валидации аргументов. Оставив эти
аннотации в коде, мы бы усложнили себе
жизнь в будущем, обеспечивая работу
таких автоматических средств.
Опять много
слов, а суть простая. Здесь вы видите
небольшой набор best practice по работе с
разными примитивами языка. Это, конечно,
не укладывается в предназначение
документа «Style Guide for Python Code», но мы же
помним, «A Foolish Consistency is the Hobgoblin of Little
Minds».
На этом PEP 8
как-то заканчивается.
В начале
кодирования вам придется иногда
использовать этот материал как справочник,
но с наработкой практики заглядывать
в него придется все реже и реже.
original post http://vasnake.blogspot.com/2014/09/pep-8-programming-recommendations.html
Комментариев нет:
Отправить комментарий