Самоучитель PythonВыпуск 2Дмитрий Мусинмая 07, 2017
Скачать 0.74 Mb.
|
Константы Константы обычно объявляются на уровне модуля и записываются только заглавными буквами, а слова разделяются символами подчеркивания. Например: MAX_OVERFLOW, TOTAL. Проектирование наследования Обязательно решите, каким должен быть метод класса или экземпляра класса (далее - ат- рибут) — публичный или непубличный. Если вы сомневаетесь, выберите непубличный атрибут. Потом будет проще сделать его публичным, чем наоборот. Публичные атрибуты — это те, которые будут использовать другие программисты, и вы должны быть уверены в отсутствии обратной несовместимости. Непубличные атрибу- ты, в свою очередь, не предназначены для использования третьими лицами, поэтому вы можете не гарантировать, что не измените или не удалите их. Мы не используем термин “приватный атрибут”, потому что на самом деле в python та- ких не бывает. 25.6. Соглашения по именованию 95 Самоучитель Python, Выпуск 0.2 Другой тип атрибутов классов принадлежит так называемому API подклассов (в других языках они часто называются protected). Некоторые классы проектируются так, чтобы от них наследовали другие классы, которые расширяют или модифицируют поведение базового класса. Когда вы проектируете такой класс, решите и явно укажите, какие атри- буты являются публичными, какие принадлежат API подклассов, а какие используются только базовым классом. Теперь сформулируем рекомендации: • Открытые атрибуты не должны иметь в начале имени символа подчеркивания. • Если имя открытого атрибута конфликтует с ключевым словом языка, добавьте в конец имени один символ подчеркивания. Это более предпочтительно, чем аббре- виатура или искажение написания (однако, у этого правила есть исключение — ар- гумента, который означает класс, и особенно первый аргумент метода класса (class method) должен иметь имя cls). • Назовите простые публичные атрибуты понятными именами и не пишите сложные методы доступа и изменения (accessor/mutator, get/set, — прим. перев.) Помните, что в python очень легко добавить их потом, если потребуется. В этом случае используй- те свойства (properties), чтобы скрыть функциональную реализацию за синтаксисом доступа к атрибутам. Примечание 1: Свойства (properties) работают только в классах нового стиля (в Python 3 все классы являются таковыми). Примечание 2: Постарайтесь избавиться от побочных эффектов, связанным с функ- циональным поведением; впрочем, такие вещи, как кэширование, вполне допусти- мы. Примечание 3: Избегайте использования вычислительно затратных операций, по- тому что из-за записи с помощью атрибутов создается впечатление, что доступ про- исходит (относительно) быстро. • Если вы планируете класс таким образом, чтобы от него наследовались другие клас- сы, но не хотите, чтобы подклассы унаследовали некоторые атрибуты, добавьте в имена два символа подчеркивания в начало, и ни одного — в конец. Механизм из- менения имен в python сработает так, что имя класса добавится к имени такого ат- рибута, что позволит избежать конфликта имен с атрибутами подклассов. Примечание 1: Будьте внимательны: если подкласс будет иметь то же имя класса и имя атрибута, то вновь возникнет конфликт имен. Примечание 2: Механизм изменения имен может затруднить отладку или работу с __getattr__(), однако он хорошо документирован и легко реализуется вручную. Примечание 3: Не всем нравится этот механизм, поэтому старайтесь достичь ком- промисса между необходимостью избежать конфликта имен и возможностью до- ступа к этим атрибутам. 25.6. Соглашения по именованию 96 Самоучитель Python, Выпуск 0.2 Общие рекомендации • Код должен быть написан так, чтобы не зависеть от разных реализаций языка (PyPy, Jython, IronPython, Pyrex, Psyco и пр.). Например, не полагайтесь на эффективную реализацию в CPython конкатенации строк в выражениях типа a+=b или a=a+b. Такие инструкции выполняются значи- тельно медленнее в Jython. В критичных к времени выполнения частях программы используйте ‘’.join() — таким образом склеивание строк будет выполнено за линей- ное время независимо от реализации python. • Сравнения с None должны обязательно выполняться с использованием операторов is или is not, а не с помощью операторов сравнения. Кроме того, не пишите if x, если имеете в виду if x is not None — если, к примеру, при тестировании такая переменная может принять значение другого типа, отличного от None, но при приведении типов может получиться False! • При реализации методов сравнения, лучше всего реализовать все 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 • Наследуйте свой класс исключения от Exception, а не от BaseException. Прямое на- следование от BaseException зарезервировано для исключений, которые не следует перехватывать. • Используйте цепочки исключений соответствующим образом. В Python 3, “raise X from Y” следует использовать для указания явной замены без потери отладочной информации. Когда намеренно заменяется исключение (использование “raise X” в Python 2 или “raise X from None” в Python 3.3+), проследите, чтобы соответствующая информация передалась в новое исключение (такие, как сохранение имени атрибута при преоб- разовании KeyError в AttributeError или вложение текста исходного исключения в новом). 25.7. Общие рекомендации 97 Самоучитель Python, Выпуск 0.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”. • При связывании перехваченных исключений с именем, предпочитайте явный син- таксис привязки, добавленный в Python 2.6: try : process_data() except Exception as exc: raise DataProcessingFailedError( str (exc)) Это единственный синтаксис, поддерживающийся в Python 3, который позволяет из- бежать проблем неоднозначности, связанных с более старым синтаксисом на основе запятой. • При перехвате ошибок операционной системы, предпочитайте использовать явную иерархию исключений, введенную в Python 3.3, вместо анализа значений errno. • Постарайтесь заключать в каждую конструкцию try...except минимум кода, чтобы легче отлавливать ошибки. Опять же, это позволяет избежать замаскированных ошибок. Правильно: 25.7. Общие рекомендации 98 Самоучитель Python, Выпуск 0.2 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 для того, чтобы после выполнения он был очищен оперативно и надёжно. • Менеджеры контекста следует вызывать с помощью отдельной функции или мето- да, всякий раз, когда они делают что-то другое, чем получение и освобождение ре- сурсов. Например: Правильно: with conn begin_transaction(): do_stuff_in_transaction(conn) Неправильно: with conn: do_stuff_in_transaction(conn) Последний пример не дает никакой информации, указывающей на то, что __enter__ и __exit__ делают что-то кроме закрытия соединения после транзакции. Быть явным важно в данном случае. • Используйте строковые методы вместо модуля string — они всегда быстрее и имеют тот же API для unicode-строк. Можно отказаться от этого правила, если необходима совместимость с версиями python младше 2.0. В Python 3 остались только строковые методы. • Пользуйтесь ‘’.startswith() и ‘’.endswith() вместо обработки срезов строк для проверки суффиксов или префиксов. startswith() и endswith() выглядят чище и порождают меньше ошибок. Например: Правильно: if foo startswith( 'bar' ): 25.7. Общие рекомендации 99 Самоучитель Python, Выпуск 0.2 Неправильно: 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 : Совсем неправильно: 25.7. Общие рекомендации 100 Самоучитель Python, Выпуск 0.2 if greeting is True : 25.7. Общие рекомендации 101 Глава 26 Документирование кода в Python. PEP 257 Документирование кода в python - достаточно важный аспект, ведь от нее порой зави- сит читаемость и быстрота понимания вашего кода, как другими людьми, так и вами через полгода. PEP 257 описывает соглашения, связанные со строками документации python, рассказы- вает о том, как нужно документировать python код. Цель этого PEP - стандартизировать структуру строк документации: что они должны в себя включать, и как это написать (не касаясь вопроса синтаксиса строк документации). Этот PEP описывает соглашения, а не правила или синтаксис. При нарушении этих соглашений, самое худшее, чего можно ожидать - некоторых неодобрительных взглядов. Но некоторые программы (например, docutils), знают о со- глашениях, поэтому следование им даст вам лучшие результаты. Что такое строки документации? Строки документации - строковые литералы, которые являются первым оператором в мо- дуле, функции, классе или определении метода. Такая строка документации становится специальным атрибутом __doc__ этого объекта. Все модули должны, как правило, иметь строки документации, и все функции и классы, экспортируемые модулем также должны иметь строки документации. Публичные ме- тоды (в том числе __init__) также должны иметь строки документации. Пакет модулей может быть документирован в __init__.py. Для согласованности, всегда используйте “”“triple double quotes”“” для строк документа- ции. Используйте r”“”raw triple double quotes”“”, если вы будете использовать обратную косую черту в строке документации. 102 Самоучитель Python, Выпуск 0.2 Существует две формы строк документации: однострочная и многострочная. Однострочные строки документации Однострочники предназначены для действительно очевидных случаев. Они должны умещаться на одной строке. Например: def kos_root (): """Return the pathname of the KOS root directory.""" global _kos_root if _kos_root: return _kos_root Используйте тройные кавычки, даже если документация умещается на одной строке. По- том будет проще её дополнить. Закрывающие кавычки на той же строке. Это смотрится лучше. Нет пустых строк перед или после документации. Однострочная строка документации не должна быть “подписью” параметров функции / метода (которые могут быть получены с помощью интроспекции). Не делайте: def function (a, b): """function(a, b) -> list""" Этот тип строк документации подходит только для C функций (таких, как встроенные модули), где интроспекция не представляется возможной. Тем не менее, возвращаемое значение не может быть определено путем интроспекции. Предпочтительный вариант для такой строки документации будет что-то вроде: def function (a, b): """Do X and return a list.""" (Конечно, “Do X” следует заменить полезным описанием!) Многострочные строки документации Многострочные строки документации состоят из однострочной строки документации с последующей пустой строкой, а затем более подробным описанием. Первая строка мо- жет быть использована автоматическими средствами индексации, поэтому важно, что- бы она находилась на одной строке и была отделена от остальной документации пустой строкой. Первая строка может быть на той же строке, где и открывающие кавычки, или на следующей строке. Вся документация должна иметь такой же отступ, как кавычки на первой строке (см. пример ниже). Вставляйте пустую строку до и после всех строк документации (однострочных или много- строчных), которые документируют класс - вообще говоря, методы класса разделены друг от друга одной пустой строкой, а строка документации должна быть смещена от первого 26.2. Однострочные строки документации 103 Самоучитель Python, Выпуск 0.2 метода пустой строкой; для симметрии, поставьте пустую строку между заголовком клас- са и строкой документации. Строки документации функций и методов, как правило, не имеют этого требования. Строки документации скрипта (самостоятельной программы) должны быть доступны в качестве “сообщения по использованию”, напечатанной, когда программа вызывается с некорректными или отсутствующими аргументами (или, возможно, с опцией “-h”, для помощи). Такая строка документации должна документировать функции программы и синтаксис командной строки, переменные окружения и файлы. Сообщение по использо- ванию может быть довольно сложным (несколько экранов) и должно быть достаточным для нового пользователя для использования программы должным образом, а также пол- ный справочник со всеми вариантами и аргументами для искушенного пользователя. Строки документации модуля должны, как правило, перечислять классы, исключения, функции (и любые другие объекты), которые экспортируются модулем, с краткими пояс- нениями (в одну строчку) каждого из них. (Эти строки, как правило, дают меньше дета- лей, чем первая строка документации к объекту). Строки документации пакета модулей (т.е. строка документации в __init__.py) также должны включать модули и подпакеты. Строки документации функции или метода должны обобщить его поведение и докумен- тировать свои аргументы, возвращаемые значения, побочные эффекты, исключения, до- полнительные аргументы, именованные аргументы, и ограничения на вызов функции. Строки документации класса обобщают его поведение и перечисляют открытые мето- ды и переменные экземпляра. Если класс предназначен для подклассов, и имеет допол- нительный интерфейс для подклассов, этот интерфейс должен быть указан отдельно (в строке документации). Конструктор класса должен быть задокументирован в документа- ции метода __init__. Отдельные методы должны иметь свои строки документации. Если класс - подкласс другого класса, и его поведение в основном унаследовано от этого класса, строки документации должны отмечать это и обобщить различия. Используйте глагол “override”, чтобы указать, что метод подкласса заменяет метод суперкласса и не вызывает его; используйте глагол “extend”, чтобы указать, что метод подкласса вызывает метод суперкласса (в дополнение к собственному поведению). И, напоследок, пример: def complex (real = 0.0 , imag = 0.0 ): """Form a complex number. Keyword arguments: real -- the real part (default 0.0) imag -- the imaginary part (default 0.0) """ if imag == 0.0 and real == 0.0 : return complex_zero А ещё больше примеров можно посмотреть в стандартной библиотеке python (например, в папке Lib вашего интерпретатора python). 26.3. Многострочные строки документации 104 Глава 27 Работа с модулями: создание, подключение инструкциями import и from Модулем в Python называется любой файл с программой (да-да, все те программы, кото- рые вы писали, можно назвать модулями). В этой статье мы поговорим о том, как создать модуль, и как подключить модуль, из стандартной библиотеки или написанный вами. Каждая программа может импортировать модуль и получить доступ к его классам, функ- циям и объектам. Нужно заметить, что модуль может быть написан не только на Python, а например, на C или C++. |