Как устроен Python. Как устроен Python. Харрисон. Харрисон Мэтт
Скачать 5.41 Mb.
|
232 Глава 23. Исключения 23.8. Определение собственных исключений В модуле exceptions определено много встроенных исключений. Если ваша ошибка хорошо соответствует какому-то существующему исключе- нию, используйте его. Ниже представлена иерархия классов встроенных исключений: BaseException SystemExit KeyboardInterrupt GeneratorExit Exception StopIteration ArithmeticError FloatingPointError OverflowError ZeroDivisionError AssertionError AttributeError BufferError EnvironmentError IOError OSError EOFError ImportError LookupError IndexError KeyError MemoryError NameError UnboundLocalError ReferenceError RuntimeError NotImplementedError SyntaxError IndentationError TabError SystemError TypeError ValueError UnicodeError UnicodeDecodeError 23.9. Итоги 233 UnicodeEncodeError UnicodeTranslateError Warning DeprecationWarning PendingDeprecationWarning RuntimeWarning SyntaxWarning UserWarning FutureWarning ImportWarning UnicodeWarning BytesWarning Для определения собственных исключений субклассируйте класс Exception или один из его субклассов. Дело в том, что все остальные субклассы BaseException не обязательно являются «исключениями». Например, если вы перехватываете KeyboardInterrupt , вам не удастся прервать выполнение процесса клавишами Ctrl+C . Если вы перехватите GeneratorExit , перестанут работать генераторы. Вот как выглядит исключение для определения нехватки информации в программе: >>> class DataError(Exception): ... def __init__(self, missing): ... self.missing = missing Использовать нестандартное исключение достаточно просто: >>> if 'important_data' not in config: ... raise DataError('important_data missing') 23.9. Итоги В этой главе были представлены стратегии обработки исключений. В стратегии LBYL перед выполнением операции вы проверяете, что в текущей ситуации не будет выдана ошибка. В стратегии EAFP весь код, который может выдать ошибку, заключается в блок try / catch . В Python предпочтение отдается второму стилю программирования. Также были рассмотрены различные механизмы перехвата ошибок, их выдачи и повторной выдачи. В завершение главы было показано, как 234 Глава 23. Исключения субклассировать существующие исключения для создания ваших соб- ственных исключений. 23.10. Упражнения 1. Напишите программу, выполняющую функции простейшего каль- кулятора. Программа получает два числа, а затем запрашивает опе- ратор. Обеспечьте корректную обработку ввода, который не преоб- разуется в числа. Обработайте ошибки деления на ноль. 2. Напишите программу, которая вставляет нумерацию перед строка- ми файла. Имя файла передается программе в командной строке. Импортируйте модуль sys и прочитайте имя файла из списка sys. argv . Корректно обработайте возможность передачи несуществую- щего файла. 24 Импортирование библиотек В предыдущих главах были рассмотрены основные конструкции языка Python. Эта глава посвящена импортированию кода. Во многих языках существует концепция библиотек, или блоков кода, предназначенных для повторного использования. Python поставляется с большой подборкой библиотек, однако, чтобы извлечь пользу из этих библиотек, вы должны уметь пользоваться ими. Чтобы использовать библиотеку, нужно загрузить код из библиотеки в пространство имен вашей программы. Пространство имен содержит функции, классы и переменные, доступные для программы. Если вы хо- тите вычислить синус угла, вам придется определить функцию, которая выполняет эти вычисления, или же загрузить готовую функцию. Встро- енная библиотека math содержит функцию sin для вычисления синуса угла, выраженного в радианах. Библиотека также содержит переменную, определяющую значение «пи»: >>> from math import sin, pi >>> sin(pi/2) 1.0 Приведенный фрагмент загружает модуль math . Тем не менее он не поме- щает math в ваше пространство имен. Вместо этого он создает переменную, которая указывает на функцию sin из модуля math . Он также создает переменную, указывающую на переменную pi из модуля math . Если вы 236 Глава 24. Импортирование библиотек проанализируете текущее пространство имен при помощи функции dir , вы сможете убедиться в этом: >>> 'sin' in dir() True 24.1. Способы импортирования В предыдущем примере мы импортировали одну функцию из библио- теки. Также можно загрузить библиотеку в пространство имен и обра- щаться ко всем классам, функциям и переменным. Чтобы импортировать модуль math в пространство имен, введите следующую команду: >>> import math В этом примере импортируется библиотека math . При этом создается новая переменная math , указывающая на модуль. Этот модуль содержит разные атрибуты, список которых можно вывести функцией dir : >>> dir(math) ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc'] Большинство этих атрибутов составляют функции. Если вы хотите вы- звать функцию tan , сделать это не удастся, потому что имя tan не входит в ваше пространство имен — только math . Однако вы можете провести поиск по переменной math с использованием оператора «точка» ( ). Этот оператор просматривает атрибуты объекта. Так как в Python нет ничего, кроме объектов, вы можете воспользоваться этим оператором для поиска атрибута tan объекта math : >>> math.tan(0) 0.0 24.1. Способы импортирования 237 Импорт библиотек import math tan sin Id:1aea math Id:1ca8 __class__:function __class__:module Id:1ca8 Код Что делает компьютер Переменные Объекты __class__:function Рис. 24.1. Импорт модуля. Обратите внимание, что этот код создает новую переменную math, которая указывает на модуль. Модуль имеет различные атрибуты, к которым вы можете получить доступ, использовав период Если вы захотите прочитать документацию по функции tan , восполь- зуйтесь функцией help : >>> help(math.tan) Справка по встроенной функции tan в модуле math: tan(...) tan(x) Возвращает тангенс x (в радианах). 238 Глава 24. Импортирование библиотек СОВЕТ Когда импортировать функцию с использованием from , а когда следует вы- брать команду import ? Если вы используете только пару атрибутов из библи- отеки, используйте механизм импортирования в стиле from . В конструкции from можно перечислить несколько атрибутов, разделенных запятыми: >>> from math import sin, cos, tan >>> cos(0) 1.0 Но если вам нужно обращаться к большей части библиотеки, будет проще импортировать библиотеку командой import . Такое решение также подскажет каждому, кто будет читать ваш код (включая вас), откуда взялась та или иная функция (класс, переменная). ПРИМЕЧАНИЕ Если вам нужно импортировать несколько атрибутов из библиотеки, команда может занять несколько строк. Если вы попробуете продолжить импортиро- вание функций в следующей строке, произойдет ошибка: >>> from math import sin, ... cos Traceback (most recent call last): File " SyntaxError: trailing comma not allowed without surrounding parentheses Чтобы показать, что команда продолжается в следующей строке, поставьте символ \ : >>> from math import sin,\ ... cos Также возможен другой вариант — перечислить импортируемые имена в кру- глых скобках. Открывающая круглая скобка (или квадратная, или фигурная) показывает, что команда продолжается в следующей строке: >>> from math import (sin, ... cos) Последняя форма считается более идиоматичной для Python. 24.2. Конфликты имен при импортировании 239 24.2. Конфликты имен при импортировании Если вы работаете над программой, выполняющей тригонометрические операции, у вас уже может быть определена функция с именем sin . А если вы также захотите использовать функцию sin из библиотеки math ? Одно из возможных решений — импортировать math ; тогда запись math.sin будет обозначать библиотечную версию, а sin — вашу функцию. Python также предоставляет другую возможность. Вы можете переопре- делить импортируемое имя при помощи ключевого слова as : >>> from math import sin as other_sin >>> other_sin(0) 0.0 Теперь other_sin представляет собой ссылку на версию sin из math , а вы можете продолжать использовать sin без необходимости рефакторинга кода. Ключевое слово as также работает с командами import . Если у вас имеется переменная (или функция), имя которой конфликтовало с именем из math в вашем пространстве имен, следующий фрагмент демонстрирует одно из возможных решений: >>> import math as other_math >>> other_math.sin(0) 0.0 СОВЕТ Ключевое слово as также поможет сделать код более компактным. Если в вашей любимой библиотеке используются слишком длинные имена, вы можете легко сократить их в своем коде. Пользователи библиотеки Numpy 1 приняли в качестве стандарта сокращенные имена из двух букв: >>> import numpy as np Аналогичный стандарт был принят в библиотеке Pandas 2 : >>> import pandas as pd 1 numpy.org 2 pandas.pydata.org 240 Глава 24. Импортирование библиотек 24.3. Массовое импортирование Python также позволяет загромоздить пространство имен так называе- мым массовым импортированием: >>> from math import * >>> asin(0) 0.0 В этом коде вызывается функция арксинуса, которая не была определена в коде. Та строка, в которой вызывается asin , содержит первое упоми- нание asin в коде. Что произошло? Когда вы используете команду math import * , эта команда приказывает Python загрузить все содержимое библиотеки math (определения классов, функции и переменные) в ло- кальное пространство имен. Хотя такая возможность на первый взгляд кажется удобной, на самом деле она довольно опасна. Массовое импортирование усложняет отладку, потому что оно не вы- ражает явно, откуда взялся тот или иной код. Еще больше проблем воз- никает с массовым импортированием из нескольких библиотек. Более поздние команды импортирования могут переопределить что-то такое, что было определено в более ранней библиотеке. По этой причине ис- пользовать массовое импортирование не рекомендуется. СОВЕТ Не используйте массовое импортирование! Возможные исключения из этого правила встречаются разве что при само- стоятельном написании кода тестирования или при экспериментах в REPL. Авторы библиотек применяют эту конструкцию для импортирования всего содержимого библиотеки, которую они собираются протестировать. Не используйте массовое импортирование только потому, что вы увидели его в чужом коде. Вспомните Дзен Python: «Явное лучше, чем неявное». 24.5. Организация импортирования 241 24.4. Вложенные библиотеки У некоторых пакетов Python есть вложенное пространство имен. Напри- мер, библиотека XML, входящая в поставку Python, включает поддержку minidom и etree . Обе библиотеки размещаются внутри родительского пакета xml : >>> from xml.dom.minidom import \ ... parseString >>> dom = parseString( ... ' >>> from xml.etree.ElementTree import \ ... XML >>> elem = XML(' Конструкция from позволяет импортировать только нужные функции и классы. Конструкция import (без from ) увеличивает объем кода (но также открывает доступ ко всему содержимому пакета): >>> import xml.dom.minidom >>> dom = xml.dom.minidom.parseString( ... ' >>> import xml.etree.ElementTree >>> elem = xml.etree.ElementTree.XML( ... ' 24.5. Организация импортирования Согласно PEP 8, команды импортирования должны располагаться в на- чале файла за строкой документации модуля. В каждой строке должна находиться одна команда import , причем команды import должны быть объединены в группы: Импортирование из стандартной библиотеки. Импортирование из стороннего кода. Импортирование из локальных пакетов. 242 Глава 24. Импортирование библиотек Например, начало модуля может выглядеть так: #!/usr/bin/env python3 """ Модуль преобразует записи в JSON и сохраняет их в базе данных """ import json # Стандартные библиотеки import sys import psycopg2 # Сторонние библиотеки import recordconverter # Локальные библиотеки СОВЕТ Сгруппированные команды импортирования удобно упорядочить по алфа- виту. СОВЕТ Иногда команды импортирования бывает полезно отложить по следующим причинам: • Для предотвращения циклического импортирования (ситуации, при кото- рой модули импортируют друг друга). Если вы не можете (или не хотите) провести рефакторинг для предотвращения циклического импортирова- ния, команду import можно разместить в функции или методе с тем кодом, из которого вызываются импортируемые элементы. • Для предотвращения импортирования модулей, недоступных в некоторых системах. • Для предотвращения импортирования больших модулей, не используе- мых в программе. 24.7. Упражнения 243 24.6. Итоги В этой главе рассматривалось импортирование библиотек в Python. У Python имеется большая стандартная библиотека 1 , и вам нужно будет импортировать эти библиотеки для использования в коде. В книге не- однократно подчеркивалось, что в Python нет ничего, кроме объектов, и вы часто создаете переменные, указывающие на эти объекты. При импортировании модуля создается переменная, указывающая на объект модуля. После этого вы можете обратиться к любым элементам про- странства имен модуля при помощи операции поиска ( ). Также можно избирательно импортировать части пространства имен модуля командой from . Если вы хотите переименовать импортируемый элемент, воспользуйтесь конструкцией as 24.7. Упражнения 1. Найдите в стандартной библиотеке Python пакет для работы с JSON. Импортируйте библиотечный модуль и просмотрите атрибуты. Используйте функцию help для получения более подробной инфор- мации об использовании модуля. Сериализуйте словарь, связываю- щий ключ 'name' с вашим именем и ключ 'age' с вашим возрастом, в строку JSON. Проведите десериализацию JSON в Python. 2. Найдите в стандартной библиотеке Python пакет для вывода со- держимого каталогов. Используйте этот пакет и напишите функ- цию, которая получает имя каталога, получает список всех файлов в этом каталоге и выводит отчет с количеством файлов для каждо- го расширения. 1 https://docs.python.org/3/library/index.html 25 Библиотеки: пакеты и модули В главе 24 вы научились импортировать библиотеки. В этой главе мы разберемся в том, что же представляет собой библиотека. При импорти- ровании библиотеки действуют два требования: 1. Библиотека должна представлять собой модуль или пакет. 2. Библиотека должна находиться в месте, определяемом переменной среды PYTHONPATH или переменной Python sys.path 25.1. Модули Модули представляют собой файлы Python с расширением .py и именем, пригодным для импортирования. Согласно PEP 8, имена файлов модулей должны быть короткими и записываться в нижнем регистре. Символы подчеркивания могут использоваться для удобства чтения. 25.2. Пакеты Пакет в Python представляет собой каталог, содержащий файл с именем __init__.py . Файл __init__.py может содержать любую реализацию (или во- обще быть пустым). Кроме того, каталог может содержать произвольное количество модулей и субпакетов. 25.3. Импортирование пакетов 245 Что лучше использовать при написании кода — модуль или пакет? Я обычно начинаю с более простого варианта и использую модуль. А если мне нужно выделять отдельные части в собственные модули, я преоб- разую модули в пакет. Пример структуры каталога из популярного проекта SQLAlchemy 1 (сред- ство объектно-реляционного отображения для баз данных): sqlalchemy/ __init__.py engine/ __init__.py base.py schema.py Согласно PEP 8, имена каталогов пакетов должны быть короткими и записываться в нижнем регистре. Символы подчеркивания в них не- допустимы. 25.3. Импортирование пакетов Чтобы импортировать пакет, используйте команду import с именем пакета (именем каталога): >>> import sqlalchemy Эта команда импортирует файл sqlalchemy/__init__.py в текущее простран- ство имен, если пакет будет найден в PYTHONPATH или sys.path Если вы захотите использовать классы Column и ForeignKey из модуля schema.py , подойдет любой из следующих фрагментов. Первый включает sqlalchemy.schema в ваше пространство имен, а второй помещает в про- странство имен только schema : >>> import sqlalchemy.schema >>> col = sqlalchemy.schema.Column() >>> fk = sqlalchemy.schema.ForeignKey() 1 https://www.sqlalchemy.org/ 246 Глава 25. Библиотеки: пакеты и модули или >>> from sqlalchemy import schema >>> col = schema.Column() >>> fk = schema.ForeignKey() А если вам нужен только класс Column , импортируйте этот класс одним из двух способов: >>> import sqlalchemy.schema.Column >>> col = sqlalchemy.schema.Column() или >>> from sqlalchemy.schema import Column >>> col = Column() 25.4. PYTHONPATH Переменная среды PYTHONPATH содержит список нестандартных катало- гов, в которых Python ищет модули или пакеты. Обычно эта перемен- ная пуста. Изменять PYTHONPATH обычно не обязательно, если только вы в процессе разработки не хотите использовать библиотеки, которые еще не были установлены. |