Как устроен Python. Как устроен Python. Харрисон. Харрисон Мэтт
Скачать 5.41 Mb.
|
СОВЕТ Оставьте переменную PYTHONPATH пустой, если только у вас нет веских при- чин для ее изменения. В этом разделе показано, что может произойти при ее изменении. Последствия могут сбить с толку других разработчиков, пытаю- щихся отладить ваш код, если они забудут о том, что переменная PYTHONPATH была изменена. Если вы включили код в файл /home/test/a/plot.py , но работали из каталога /home/test/b/ , использование PYTHONPATH предоставит доступ к этому коду. В противном случае, если файл plot.py не был установлен системными средствами или средствами Python, попытка его импортирования при- ведет к ошибке ImportError : >>> import plot Traceback (most recent call last): 25.5. sys.path 247 File " ImportError: No module named plot Если вы запустите Python с присваиванием значения переменной PYTHONPATH , она укажет Python, где нужно искать библиотеки: $ PYTHONPATH=/home/test/a python3 Python 3.6.0 (default, Dec 24 2016, 08:01:42) >>> import plot >>> plot.histogram() СОВЕТ Для установки пакетов Python могут использоваться менеджеры пакетов, исполняемые файлы Windows или специализированные средства Python, такие как pip 1 25.5. sys.path У модуля sys имеется атрибут path со списком каталогов, в которых Python ищет библиотеки. Просмотрев sys.path , вы увидите все просмо- тренные каталоги: >>> import sys >>> sys.path ['', '/usr/lib/python35.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/plat-darwin', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages'] СОВЕТ Если вы сталкиваетесь с ошибкой вида ImportError: No module named plot, обратитесь к переменной sys.path и посмотрите, присутствует ли в ней каталог, в котором находится файл foo.py (если это модуль). Если plot 1 https://pip.pypa.io/ 248 Глава 25. Библиотеки: пакеты и модули является пакетом, то каталог plot/ должен находиться в одном из каталогов из sys.path : >>> import plot Traceback (most recent call last): File " ImportError: No module named plot >>> sys.path.append('/home/test/a') >>> import plot >>> plot.histogram() Также можно включить этот каталог в PYTHONPATH из командной строки, ис- пользуемой для запуска Python. Еще раз подчеркнем, что обычно задавать вручную sys.path или PYTHONPATH не нужно. В нормальной ситуации вы устанавливаете библиотеки, а про- грамма установки размещает их в правильном каталоге. СОВЕТ Если вы хотите узнать местонахождение библиотеки в файловой системе, проверьте атрибут __file__ : >>> import json >>> json.__file__ '/usr/lib/python3.6/json/__init__.py' Этот способ работает только с библиотеками, реализованными на Python. Модуль sys не реализован на Python, поэтому эта попытка завершается неудачей: >>> import sys >>> sys.__file__ Traceback (most recent call last): AttributeError: module 'sys' has no attribute '__file__' 25.6. Итоги В этой главе рассматриваются модули и пакеты. Модуль представляет собой файл Python. Пакет представляет собой каталог, в котором при- 25.7. Упражнения 249 сутствует файл с именем __init__.py . Пакет также может содержать другие модули и пакеты. Существует список путей, который определяет, к каким каталогам об- ращается Python для импортирования библиотек. Этот список хранится в sys.path . Проверив значение этой переменной, вы узнаете, какие ката- логи просматривает Python. Вы также можете обновить этот список при помощи переменной PYTHONPATH или же внести изменения напрямую. Но обычно вместо изменения этой переменной вы используете специализи- рованные средства установки пакетов, например pip. 25.7. Упражнения 1. Создайте модуль begin.py , который содержит функцию с именем prime . Функция prime должна получать число и возвращать логиче- ский признак того, является ли число простым (то есть делится ли оно только на 1 и на себя). Перейдите в другой каталог, запустите Python и выполните команду from begin import prime Попытка должна завершиться неудачей. Обновите переменную sys. path , чтобы вы могли импортировать функцию из модуля. Затем присвойте значение PYTHONPATH 2. Создайте пакет utils . В файле __init__.py разместите код prime из предыдущего примера. Перейдите в другой каталог в терминале, запустите Python и выполните команду from utils import prime Попытка должна завершиться неудачей. Обновите переменную sys.path , чтобы вы могли импортировать функцию из пакета. Затем присвойте значение PYTHONPATH 26 Полноценный пример В этой главе вы узнаете, как организовать код в сценарии. В ней при- водится исходный код упрощенной реализации команды UNIX cat Команда cat читает имена файлов из командной строки и выводит их содержимое на экран. Предусмотрены различные параметры для вы- полнения таких операций, как добавление нумерации. Этот сценарий показывает, как организован код в типичном файле Python. 26.1. cat.py Ниже приведена реализация команды UNIX cat на языке Python. Вклю- чен параметр для добавления нумерации строк ( --number ), но другие параметры cat не поддерживаются. Сохраните код в файле с именем cat.py : #!/usr/bin/env python3 r"""Простая реализация команды unix ``cat``. Поддерживается только параметр ``--number``. Пример демонстрирует структуру файла и полезные приемы программирования на Python. Строка документации в тройных кавычках для всего модуля (этот файл). Если импортировать этот модуль и выполнить команду ``help(cat)``, вы сможете убедиться в этом. Строка документации также содержит ``документирующий тест``, 26.1. cat.py 251 который служит примером программного использования кода. Модуль ``doctest`` может выполнить эту строку документации и проверить ее правильность по выходным данным. >>> import io >>> fin = io.StringIO(\ ... 'hello\nworld\n') >>> fout = io.StringIO() >>> cat = Catter([fin], ... show_numbers=True) >>> cat.run(fout) >>> print(fout.getvalue()) 1 hello 2 world """ import argparse import logging import sys __version__ = '0.0.1' logging.basicConfig( level=logging.DEBUG) class Catter(object): """ Класс для конкатенации файлов в стандартном выводе Строка документации класса, выводится командой ``help(cat.Catter)`` """ def __init__(self, files, show_numbers=False): self.files = files self.show_numbers = show_numbers def run(self, fout): # Использовать 6 пробелов для чисел # с выравниванием по правому краю fmt = '{0:>6} {1}' 252 Глава 26. Полноценный пример for fin in self.files: logging.debug('catting {0}'.format(fin)) for count, line in enumerate(fin, 1): if self.show_numbers: fout.write(fmt.format( count, line)) else: fout.write(line) def main(args): """ Логика выполнения cat с аргументами """ parser = argparse.ArgumentParser( description='Concatenate FILE(s), or ' 'standard input, to standard output') parser.add_argument('--version', action='version', version=__version__) parser.add_argument('-n', '--number', action='store_true', help='number all output lines') parser.add_argument('files', nargs='*', type=argparse.FileType('r'), default=[sys.stdin], metavar='FILE') parser.add_argument('--run-tests', action='store_true', help='run module tests') args = parser.parse_args(args) if args.run_tests: import doctest doctest.testmod() else: cat = Catter(args.files, args.number) cat.run(sys.stdout) logging.debug('done catting') if __name__ == '__main__': main(sys.argv[1:]) Если вам не хочется вводить весь этот код вручную, загрузите его копию из интернета 1 1 https://github.com/mattharrison/IllustratedPy3/ 26.2. Что делает этот код? 253 26.2. Что делает этот код? Этот код осуществляет эхо-вывод содержимого файла (возможно, с ну- мерацией строк) на терминал в системах Windows и UNIX: $ python3 cat.py -n README.md 1 # IllustratedPy3 2 3 If you have questions or concerns, click on Issues above. Если вы запустите этот код с ключом -h , он выведет всю справочную документацию по аргументам командной строки: $ python3 cat.py -h usage: cat.py [-h] [--version] [-n] [--run-tests] [FILE [FILE ...]] Concatenate FILE(s), or standard input, to standard output positional arguments: FILE optional arguments: -h, --help show this help message and exit --version show program's version number and exit -n, --number number all output lines --run-tests run module tests Функциональность разбора командной строки реализована в функции main и обеспечивается модулем argparse из стандартной библиотеки. Модуль argparse берет на себя всю работу по разбору аргументов. Если вы хотите узнать, что делает модуль argparse , обратитесь к до- кументации в интернете или воспользуйтесь функцией help . Основная идея заключается в том, что вы создаете экземпляр класса ArgumentParser и вызываете .add_argument для каждого параметра командной строки. Вы указываете параметры командной строки, сообщаете, какое действие не- обходимо для них выполнить (по умолчанию это сохранение значения, следующего за параметром), и предоставляете справочную документа- цию. После добавления аргументов вызывается метод .parse_args для аргументов командной строки (которые берутся из sys.argv ). Резуль- тат .parse_args представляет собой объект, к которому присоединены 254 Глава 26. Полноценный пример атрибуты, имена которых определяются именами параметров. В данном случае это будут атрибуты .files и .number ПРИМЕЧАНИЕ Вы также можете воспользоваться REPL для получения информации о модуле и просмотра документации, входящей в его поставку. Помните функцию help ? Передайте ей модуль, и она выведет строку документации уровня модуля. Если вы хотите просмотреть исходный код модуля, это тоже возможно. Помните функцию dir , которая выводит атрибуты объекта? Просмотрев информацию модуля argparse , вы увидите, что у него есть атрибут __file__ Он указывает на местонахождение файла на компьютере: >>> import argparse >>> argparse.__file__ '/usr/local/Cellar/python3/3.6.0/Frameworks/ Python.framework/Versions/3.6/lib/python3.6/argparse.py' Так как код написан на Python (часть модулей написана на C), вы можете просмотреть файл с исходным кодом. К настоящему моменту вы уже сможете прочитать модуль и понять, что он должен делать. После разбора аргументов программа создает экземпляр Catter , опреде- ленный в коде, и вызывает для него метод .run Пример выглядит немного устрашающе, но весь использованный син- таксис рассматривался в книге. В оставшейся части этой главы рассма- тривается структура и другие аспекты этого кода. 26.3. Типичная структура Основные компоненты модуля Python в порядке их следования: #!/usr/bin/env python3 (если модуль также используется в качестве сценария); строка документации модуля; imports ; 26.4. #! 255 метаданные и глобальные переменные; операции с журналом; реализация; if __name__ == '__main__': (если модуль также используется в каче- стве сценария); argparse ПРИМЕЧАНИЕ Этот список — не более чем рекомендация. Большинство элементов может следовать в произвольном порядке, и не все элементы должны присутство- вать в каждом файле. Например, не каждый файл должен быть исполняемым в качестве сценария командного интерпретатора. В своих файлах вы можете применять любую структуру, но ответственность за такое решение ложится на вас. Скорее всего, пользователи вашего кода будут недовольны (или будут выпускать исправления). Любой читатель предпочтет код, построенный по общепринятым правилам, так как он сможет быстро разобраться в его логике. 26.4. #! Первая строка файла, используемого в качестве сценария, содержит по- следовательность символов #!/usr/bin/env python3 . В операционных си- стемах семейства UNIX эта строка обрабатывается для определения того, как следует выполнять сценарий. Соответственно, эта строка включается только в те файлы, которые должны выполняться в качестве сценариев. В ней должна использоваться команда python 3 , так как команда python в большинстве систем относится к Python версии 2. ПРИМЕЧАНИЕ На платформе Windows строка #! игнорируется, поэтому ее включение безопасно. Вы найдете ее во многих библиотеках, популярных в системах Windows. 256 Глава 26. Полноценный пример ПРИМЕЧАНИЕ Вместо того чтобы жестко задавать конкретный путь к исполняемому файлу Python, /usr/bin/env выбирает первый исполняемый файл python3 из ката- логов PATH пользователя. Такие средства, как venv 1 , изменяют содержимое PATH для использования альтернативных исполняемых файлов python3 ; они успешно работают в этой схеме. СОВЕТ Если в системах UNIX каталог с файлом присутствует в переменной среды PATH текущего пользователя, а файл является исполняемым, то для выпол- нения из командного интерпретатора достаточно указать только имя файла. Чтобы назначить файл исполняемым, введите следующую команду: $ chmod +x <путь/к/file.py> 26.5. Строка документации В начале файла модуля может располагаться строка документации уров- ня модуля. Она должна следовать за строкой с #! , но предшествовать любому коду Python. Строка документации содержит обзор модуля, и в ней должна содержаться сводная информация о коде. Кроме того, в ней могут содержаться примеры использования модуля. СОВЕТ Python включает библиотеку doctest для проверки примеров из интерак- тивного интерпретатора. Использование строк документации, содержащих фрагменты кода REPL, могут служить как для документации, так и для про- стой проверки логики своей библиотеки. В файле cat.py код doctest содержится в конце строки документации. При запуске cat.py с ключом --run-tests библиотека doctest перебирает все существующие строки документации и проверяет содержащийся в них код. Данная возможность приведена только для демонстрации: обычно возмож- ность выполнения тестов в сценарии не отображается для рядовых пользова- телей, не являющихся разработчиками, даже если вы включили код doctest в строку документации. В данном случае параметр --run-test включен как пример использования модуля doctest 1 https://docs.python.org/3/library/venv.html 26.7. Метаданные и глобальные переменные 257 26.6. Импортирование Команды импортирования обычно включаются в начало модулей Python. Строки import принято группировать по местонахождению библиотеки. Сначала перечисляются библиотеки, входящие в стандартную библи- отеку Python. Далее следуют сторонние библиотеки, и в последнюю очередь перечисляются библиотеки, локальные для текущего кода. Такая структура кода позволит конечным пользователям быстро просмотреть команды импорта, требования и источники происхождения кода. 26.7. Метаданные и глобальные переменные Если у вас имеются глобальные переменные уровня модуля, определите их после команд импортирования. Это позволит просмотреть модуль и быстро определить, что собой представляют глобальные переменные. Глобальные переменные определяются на уровне модуля, и они доступны в границах этого модуля. Так как Python позволяет изменить любую пере- менную, глобальные переменные являются потенциальным источником ошибок. Кроме того, код проще понять, когда переменные определяются и изменяются только в области видимости функции. Тогда вы можете быть твердо уверены в том, с какими данными вы работаете и кто их изменяет. Если глобальная переменная изменяется в нескольких местах (и особенно в разных модулях), вы своими руками готовите себе долгий сеанс отладки. Один из допустимых вариантов использования глобальных перемен- ных — эмуляция констант из других языков программирования. Кон- станта аналогична переменной, но ее значение не может изменяться. В Python не поддерживаются переменные, которые не могут изменяться, но вы можете воспользоваться специальными обозначениями, указы- вающими, что переменная должна рассматриваться в программе как доступная только для чтения. В PEP 8 указано, что имена глобальных констант должны назначаться по тем же правилам, что и имена пере- менных, но они должны быть записаны прописными буквами. Например, если вы хотите использовать в программе пропорцию золотого сечения, соответствующее значение может быть определено так: >>> GOLDEN_RATIO = 1.618 258 Глава 26. Полноценный пример Если этот код определяется в модуле, регистр символов подсказывает, что программа не должна изменять связывание этой переменной. ПРИМЕЧАНИЕ Определяя константы в виде глобальных переменных и используя тщательно продуманные имена, можно избежать проблемы, встречающейся в програм- мировании, — так называемых «волшебных чисел». Под «волшебным числом» понимается число, присутствующее в коде или формуле, которое не хранится в переменной. Это само по себе достаточно плохо, особенно когда кто-то пытается разобраться в вашем коде. Другая проблема с «волшебными числами» заключается в том, что значения со временем нередко распространяются в коде. Это не создаст проблем, пока вы не захотите изменить это значение. Что делать — провести поиск с заменой? А если «волшебное число» на самом деле представляет два разных значения — например, число сторон треугольника и размерность окружающего мира? В этом случае глобальный поиск с заменой приведет к появлению ошибок. Обе проблемы (контекст и повторение) решаются сохранением значения в именованной переменной. Таким образом у переменной образуется кон- текст, а у числа появляется имя. Оно также позволяет легко изменить зна- чение в одном месте. Кроме глобальных переменных, на этом уровне также существуют пере- менные метаданных. В метаданных хранится информация о модуле: автор, версия и т. д. Обычно метаданные хранятся в «специальных» пере- менных с двойными подчеркиваниями ( __author__ ). Например, PEP 396 рекомендует хранить версию модуля в строковой переменной __version__ на глобальном уровне модуля. |