...остальной текст удален...
Фактически, если CGI-сценарий выполняется на локальном компьютере «localhost», для просмотра одного и того же хранилища вы сможете использовать и графический интерфейс из предыдущего раздела, и вебинтерфейс из этого раздела - это всего лишь альтернативные интерфейсы доступа к одним и тем же хранимым объектам Python. Для сравнения на рис. 1.19 показано, как выглядит запись в графическом интерфейсе, которую мы видели на рис. 1.18, - это тот же самый объект, но на этот раз мы получили ее, не обращаясь к промежуточному серверу, запускающему другие сценарии или генерирующему разметку HTML.
Как и прежде, мы всегда можем проверить результаты нашей деятельности на сервере, используя интерактивную оболочку или другие сценарии. Мы можем просматривать содержимое базы данных с помощью веб-броузеров или графического интерфейса, но в любом случае это всего лишь объекты Python в файле хранилища:
>>> import shelve
>>> db = shelve.open('class-shelve')
>>> db['sue'].name
‘Sue Smith’
>>> db['guido'].job
‘BDFL’
>>> list(db['guido'].name)
[‘G’, ‘v’, ‘R’]
>>> list(db.keys())
[‘sue’, ‘bill’, ‘nobody’, ‘tomtom’, ‘tom’, ‘bob’, ‘peg’, ‘guido’]
Рис. 1.19. Тот же самый объект, отображаемый в графическом интерфейсе
Ниже приводятся результаты запуска первоначального сценария из примера 1.19, извлекающего информацию из базы данных, который мы написали до того, как перешли к реализации графического и вебинтерфейса, - в языке Python существует масса способов просмотра данных:
...\PP4E\Preview> dump_db_classes.py
sue =>
Sue Smith 60000 bill => bill 9999 nobody =>
John Doh None tomtom =>
Tom Tom 40000 tom =>
Tom Doe 90000 bob =>
Bob Smith 30000 peg =>
1 4 guido =>
GvR
Smith
Doe
Дальнейшие направления усовершенствования
Естественно, что в этот пример можно было бы внести множество улучшений:
• Разметка HTML для начальной формы ввода, представленная в примере 1.33, несколько избыточна для сценария в примере 1.34, и ее можно было бы генерировать с помощью другого сценария, используемого как источник совместно используемой информации.
• Фактически мы вообще можем отказаться от встраивания разметки HTML в наш сценарий, если воспользуемся одним из инструментов-генераторов HTML, с которыми мы познакомимся далее в книге, таким как HTMLgen (система создания разметки HTML из дерева объектов документа) и PSP (Python Server Pages - серверные страницы Python, серверная система шаблонов HTML для Python, напоминающая PHP и ASP).
• Чтобы упростить обслуживание, можно было бы также вынести разметку HTML для CGI-сценария в отдельный файл, чтобы отделить представление от логики (с разными файлами могли бы работать разные специалисты).
• Кроме того, если веб-сайтом могут пользоваться сразу несколько человек, мы могли бы добавить возможность блокировки файла хранилища или перейти на использование базы данных, такой как ZODB или MySQL, чтобы обеспечить возможность параллельных изменений. ZODB и другие полноценные системы управления базами данных позволяют также использовать возможность отмены транзакций в случае ошибок. Реализовать простейшую блокировку файла можно с помощью функции os.open и ее флагов.
• Механизмы ORM (object relational mappers - объектно-реляционного отображения) для Python, такие как SQLObject и SQLAlchemy, упоминавшиеся выше, также способны обеспечить поддержку одновременной работы нескольких пользователей с реляционной базой данных, сохраняя в ней представление данных в виде наших классов Python.
• Наконец, если размер нашего сайта станет больше, чем несколько интерактивных страниц, мы могли бы перейти от CGI-сценариев к более развитым веб-фреймворкам, таким как упоминавшиеся в начале этого раздела - Django, TurboGears, pyjamas и другие. На случай, если потребуется сохранять информацию между обращениями к страницам, можно было бы использовать такие инструменты, как cookies, скрытые поля ввода, сеансы, поддерживаемые модулем mod_python и FastCGI.
• Если потребуется хранить на сайте информационное наполнение, производимое его пользователями, мы могли бы перейти на использование Plone. Это популярная и открытая система управления содержимым, написанная на языке Python, использующая сервер приложений Zope, реализующая модель документооборота и делегирующая управление содержимым сайта его авторам.
• А если на повестке дня встанет поддержка беспроводных или распределенных интерфейсов, мы могли бы перенести нашу систему на сотовые телефоны, используя один из трансляторов с языка Python, доступных, например, для платформы Nokia и Google Android, или на платформу распределенных вычислений, такую как Google App Engine. Язык Python с успехом проникает в области, куда ведет развитие технологий.
Но, тем не менее, и графический, и веб-интерфейс, созданные нами, вполне справляются со своей работой.
Конец демонстрационного примера
На этом мы заканчиваем знакомство с вводным демонстрационным примером использования языка Python. Мы исследовали способы представления данных, ООП, механизмы сохранения объектов, инструменты создания графических интерфейсов и основы разработки веб-сайтов. Ни одну из этих тем мы не рассматривали достаточно глубоко. Тем не менее хотелось бы надеяться, что эта глава пробудила в вас любопытство к программированию приложений на языке Python.
В оставшейся части книги мы углубимся в изучение этих и других инструментов и тем прикладного программирования, чтобы помочь вам включить язык Python в работу в ваших собственных программах. В следующей главе мы начнем наше путешествие с изучения инструментов системного программирования и администрирования, имеющихся в распоряжении программистов на языке Python.
Скрытые «сюрпризы» в Python
На настоящий момент, когда я пишу эти строки в 2010 году, я занимаюсь языком Python уже почти 18 лет, и я видел, как он вырос из никому не известного языка в инструмент, который в том или ином виде используется практически каждой организацией, занимающейся разработкой, и входит в четверку или пятерку наиболее используемых языков программирования в мире. Это были лучшие годы.
Оглядываясь назад, могу сказать, что если в языке Python что-то и осталось действительно неизменным, так это его врожденная
способность заставлять разработчиков акцентировать внимание на качестве программного кода. И это практически неизбежно. Язык, который требует, чтобы разработчик форматировал программный код для большей удобочитаемости, не может не заставить людей поднимать вопросы о выборе наиболее удачных приемов разработки программного обеспечения.
Пожалуй, ничто так не подчеркивает эту сторону жизни языка Python, как модуль this из стандартной библиотеки - своего рода сюрприз, или «пасхальное яйцо» в Python, созданный одним из основных разработчиков Python - Тимом Петерсом (Tim Peters), который хранит в себе список основных принципов, на которых основывается язык. Чтобы увидеть их, запустите интерактивный сеанс интерпретатора Python и импортируйте модуль (естественно, он доступен на всех платформах):
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let’s do more of those!
>>>
(Перевод:
Дзен языка Python, составлен Тимом Петерсом
Красивое лучше, чем уродливое.
Явное лучше, чем неявное.
Простое лучше, чем сложное.
Сложное лучше, чем запутанное.
Плоское лучше, чем вложенное.
Разреженное лучше, чем плотное.
Удобочитаемость имеет значение.
Особые случаи не настолько особые, чтобы нарушать правила.
При этом практичность важнее безупречности.
Ошибки никогда не должны замалчиваться.
Если не замалчиваются явно.
Встретив двусмысленность, отбрось искушение угадать.
Должен существовать один и, желательно, только один очевидный способ
сделать что-то.
Хотя он поначалу может быть и не очевиден, если вы не голландец.
Сейчас лучше, чем никогда.
Хотя никогда зачастую лучше, чем *прямо сейчас*.
Если реализацию сложно объяснить - идея плоха.
Если реализацию легко объяснить - идея, возможно, хороша.
Пространства имен - отличная штука! Будем делать их побольше!
)1
Особого упоминания заслуживает правило «Явное лучше, чем неявное», которое в мире Python известно, как аббревиатура «EIBTI» («Explicit is better than implicit») - одна из основных идей языка Python, и одно из самых сильных отличий от других языков. Любой, кто проработал на этой ниве более, чем несколько лет, сможет засвидетельствовать, что волшебство и инженерное искусство есть вещи несовместимые. Конечно, сам язык Python не всегда неукоснительно следовал всем этим правилам, но старался придерживаться их как можно ближе. И если Python заставляет людей задумываться о таких вещах, то это уже победа. Кстати, название языка отлично смотрится на футболке.
Перевод взят из Википедии: http://ru.wikipedia.org/wiki/Python - Прим. перев.
Системное
программирование
В этой первой посвященной деталям части книги представлены инструменты Python для системного программирования - интерфейсы к службам операционной системы, а также к контексту выполнения программы. Эта часть книги состоит из следующих глав:
Глава 2
Эта глава обеспечивает полный обзор часто используемых инструментов системного программирования. Она неторопливо знакомит с инструментами и приемами, которые мы будем использовать далее в этой книге, и отчасти может использоваться, как справочник.
Глава 3
Эта глава продолжает тему, начатую в главе 2, и показывает, как работать со стандартными потоками ввода-вывода, аргументами командной строки, переменными окружения и многим другим.
Глава 4
Эта глава продолжает знакомить нас с системными интерфейсами и описывает инструменты и приемы, используемые при работе с файлами и каталогами. В этой главе мы познакомимся с двоичными файлами, с приемами обхода деревьев и так далее.
Глава5
Эта глава служит введением в поддержку библиотекой Python параллельного выполнения программ. Здесь вы найдете описание потоков выполнения, механизма ветвления процессов, каналов, сокетов, сигналов, очередей и тому подобного.
Глава 6
Последняя глава этой части содержит коллекцию типичных примеров системного программирования, основанных на материале первых четырех глав. Сценарии Python, представленные здесь, выполняют реальные задачи; демонстрируют среди прочего, как разрезать и объединять файлы, сравнивать и копировать каталоги, тестировать другие программы, а также отыскивать и запускать выполняемые файлы.
Хотя в этой части книги особое значение придается задачам системного программирования, представленные в ней средства являются универсальными и часто будут использоваться в последующих главах.
2
Системные инструменты
«os.path - дорога к знанию»
В этой главе начинается детальное рассмотрение способов применения Python для решения практических задач программирования. В этой и в последующих главах мы увидим, как использовать Python для разработки системных инструментов, графических интерфейсов пользователя, приложений баз данных, веб-сценариев, веб-сайтов и многого другого. Попутно на практических примерах будут также изучаться важнейшие концепции программирования на Python: повторное использование программного кода, простота сопровождения, объектноориентированное программирование (ООП) и так далее.
В этой части книги мы начнем свое путешествие по программированию на языке Python с исследования области системных приложений - сценариев, работающих с файлами, программами и окружением программ в целом. Хотя примеры из этой области сфокусированы на определенном типе задач, используемые в них приемы окажутся полезными и в последующих частях книги. Иными словами, если вы еще не эксперт по системному программированию на Python, вам следует пускаться в путь именно с этого места.
Зачем здесь нужен Python?
Системные интерфейсы Python обслуживают области приложений, но в последующих пяти главах большинство примеров будет относиться к категории системных инструментов - программ, иногда называемых утилитами командной строки, сценариями командной оболочки, программами системного администрирования, системными программами и другими сочетаниями этих слов. Независимо от того, знакомы
ли вам эти названия, вы, вероятно, уже знакомы со сценариями этого типа: они выполняют такие задачи, как обработка файлов в каталоге, запуск тестовых сценариев и тому подобное. Исторически такие программы писались на непереносимых и синтаксически неочевидных языках оболочек, таких как командные файлы DOS, csh и awk.
Однако даже в этой относительно простой области ярко проявляются лучшие свойства Python. Например, простота использования Python и обширное многообразие встроенных библиотек упрощают (и даже делают приятным) использование развитых системных инструментов, таких как потоки выполнения, сигналы, ветвление процессов, сокеты и аналогичные им; такие инструменты намного сложнее использовать в неясном синтаксисе языков оболочек и в многоэтапных циклах разработки на компилируемых языках. Поддержка в Python таких идей, как ясность программного кода и объектно-ориентированное программирование, способствует созданию таких инструментов оболочки, которые можно читать, сопровождать и повторно использовать. При использовании Python нет необходимости начинать с нуля каждый новый сценарий.
Более того, мы обнаружим, что в Python не только есть все интерфейсы, необходимые для разработки системных инструментов, но он также обеспечивает переносимость сценариев. При использовании стандартной библиотеки Python большинство системных сценариев, написанных на языке Python, автоматически становятся переносимыми на все основные платформы. Например, сценарий для обработки каталогов, написанный под Windows, обычно может выполняться и под Linux безо всякой правки исходных текстов: достаточно просто скопировать сценарий. Для разработки сценариев, обеспечивающих такой уровень переносимости, необходимо прикладывать некоторые усилия, тем не менее при разумном использовании Python может стать единственным средством, которым необходимо владеть для создания системных сценариев.
В следующих пяти главах
Чтобы упростить изучение этой части книги, я поделил ее на пять глав:
• В этой главе я познакомлю вас с основными системными модулями в виде краткого обзора. Здесь мы впервые встретим некоторые из наиболее часто используемых системных инструментов.
• В главе 3 мы продолжим исследование основных системных интерфейсов - изучением их роли в терминах системного программирования, таких как потоки ввода-вывода, аргументы командной строки, переменные окружения и так далее.
• В главе 4 мы сосредоточимся на изучении инструментов Python для работы с файлами, каталогами и деревьями каталогов.
• В главе 5 мы перейдем к изучению стандартных инструментов Python для реализации параллельной обработки данных - процессов, потоков выполнения, очередей, каналов, сигналов и многих других.
• В главе 6, завершающей эту часть книги, будет представлена коллекция законченных системных программ. Здесь приводятся более крупные примеры, имеющие практическую ценность; в них для решения практических задач используются инструменты, представленные в четырех предыдущих главах. В состав этой коллекции входят не только универсальные системные сценарии, но и сценарии для обработки каталогов с файлами.
В примерах, представленных в последней главе этой части книги, мы будем уделять большое внимание не только системным интерфейсам, но и принципам разработки программ на языке Python в целом. Попутно будут представлены процедурные и объектно-ориентированные версии одних и тех же примеров, чтобы показать преимущества стратегического мышления.
«Батарейки - в комплекте»
В данной главе и в следующих за ней речь идет одновременно о языке Python и о его стандартной библиотеке - коллекции модулей, написанных на языке Python и C, которые автоматически устанавливаются вместе с интерпретатором. Хотя Python и представляет собой легкий язык сценариев, большая часть операций в реальных разработках на Python выполняется с привлечением этой обширной библиотеки инструментов (по последним подсчетам - несколько сотен модулей), поставляемых вместе с пакетом Python.
В действительности стандартная библиотека обладает настолько широкими возможностями, что нередко можно слышать в отношении Python фразу «batteries included» (батарейки - в комплекте), обычно приписываемую Фрэнку Стаяно (Frank Stajano) и означающую, что все необходимое для практической повседневной деятельности уже присутствует в стандартной библиотеке и может быть импортировано. Несмотря на то, что стандартная библиотека не является частью самого языка, тем не менее она является стандартной частью системы Python, и можно быть уверенным, что она будет доступна везде, где выполняются сценарии. Фактически это одно из наиболее существенных отличий Python от некоторых других языков сценариев - благодаря тому, что в составе Python поставляется огромное количество библиотечных инструментов, для программистов на Python вспомогательные сайты не имеют такого большого значения, как CPAN для программистов на Perl.
Как будет показано далее, стандартные библиотеки выполняют существенную часть задачи программирования на Python. Овладев основами языка, вы заметите, что в основном заняты применением встроенных функций и модулей, поставляемых вместе с системой. С другой стороны, библиотеки обеспечивают все самое интересное. В реальном мире программы становятся интересными, когда в них начинают использоваться службы, находящиеся вне интерпретатора языка: сети, файлы, графические интерфейсы, базы данных и так далее. Все это поддерживается стандартной библиотекой Python.
Помимо стандартной библиотеки для Python существуют дополнительные пакеты , созданные сторонними разработчиками, которые могут быть получены и установлены отдельно. Когда писалась эта книга, большинство таких расширений сторонних разработчиков можно было найти путем поиска в Интернете и по ссылкам на http://www.python.org и на веб-сайте PyPI (ссылка на который также приводится на сайте http://www.python.org). Некоторые сторонние расширения являются крупными системами. Например, расширения NumPy, Djangо и VPython реализуют операции векторной алгебры, обеспечивают конструирование сайтов и предоставляют средства визуализации соответственно.
Если нужно сделать с помощью Python что-либо специальное, есть большая вероятность, что необходимая поддержка уже включена в состав стандартной библиотеки или реализована в виде бесплатного модуля с открытым исходным кодом. Большинство инструментов, используемых в этой книге, входит в стандартный дистрибутив Python, а то, что должно устанавливаться отдельно, будет отмечено особо. Конечно, идиома повторного использования программного кода делает программы зависимыми от используемого в них кода, однако на практике, в чем мы еще не раз убедимся в этой книге, мощные библиотеки с открытыми исходными текстами развиваются очень быстро, не запирая своих пользователей в узком кругу фиксированных возможностей и ограничений.
Знакомство с разработкой системных сценариев
Исследование области системного программирования мы начнем с краткого обзора модулей sys и os из стандартной библиотеки, а затем перейдем к более важным понятиям системного программирования. Из перечня атрибутов этих модулей можно заключить, что это очень крупные модули, - следующий пример интерактивного сеанса был получен в Python 3.1 и в Windows 7 вне среды IDLE:
C:\...\PP4E\System> python
Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (...)] on win32
Type "help”, "copyright”, "credits” or "license” for more information.
>>> import sys, os
>>> len(dir(sys)) # 65 атрибутов
65
>>> len(dir(os)) # в Windows 122 атрибута, в Unix - больше
122
>>> len(dir(os.path)) # модуль, вложенный в os 52
Содержимое этих двух модулей может отличаться для разных версий Python и платформ. Например, модуль os имеет намного больший размер после сборки Python 3.1 из исходных текстов под Cygwin (Cygwin - система, обеспечивающая Unix-подобную функциональность в Windows; о ней рассказывается во врезке «Подробнее о Cygwin Python для Windows» в главе 5):
$ ./python.exe
Python 3.1.1 (r311:74480, Feb 20 2010, 10:16:52)
[GCC 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)] on cygwin Type "help”, "copyright”, "credits” or "license” for more information.
>>> import sys, os >>> len(dir(sys))
64
>>> len(dir(os))
217
>>> len(dir(os.path))
51
Я не собираюсь представлять все элементы в каждом встроенном модуле, поэтому прежде всего я хочу показать, как самостоятельно получать более подробную информацию. Такая задача может служить формальным оправданием тому, что здесь будут представлены некоторые базовые понятия системного программирования. Попутно мы напишем код первого сценария для форматирования документации.
Системные модули Python
Большинство интерфейсов Python системного уровня находится всего в двух модулях: sys и os. Впрочем, это несколько упрощенное представление - к данной области относятся и другие стандартные модули. В их числе:
glob
Реализует механизм подстановки имен файлов
socket
Обеспечивает возможности создания сетевых соединений и взаимодействий между процессами (Inter-Process Communication, IPC)
threading, _thread, queue
Средства запуска и синхронизации параллельных потоков выполнения
time, timeit
Обеспечивают возможность получения информации о системном времени
subprocess, multiprocessing
Средства запуска и управления параллельными процессами
signal, select, shutil, tempfile и другие Для решения других системных задач
Некоторые сторонние расширения, такие как pySerial (интерфейс к последовательному порту), Pexpect (механизм управления взаимодействиями между программами, напоминающий утилиту Expect) и даже Twisted (сетевой фреймворк), также могут быть отнесены к разряду системных инструментов. Кроме того, некоторые встроенные функции также в действительности являются системными интерфейсами -функция open, например, обеспечивает интерфейс к файловой системе. Но в общем и целом ядро арсенала системных инструментов Python образуют модули sys и os.
В теории, по крайней мере, модуль sys экспортирует компоненты, относящиеся к самому интерпретатору Python (например, путь поиска модулей), a модуль os содержит переменные и функции, соответствующие операционной системе, в которой выполняется Python. На практике это различие может быть не столь отчетливым (например, стандартные потоки ввода и вывода находятся в модуле sys, но можно утверждать, что они связаны с парадигмами операционной системы). Могу вас обрадовать: инструменты, находящиеся в этих модулях, будут использоваться так часто, что их местонахождение прочно отпечатается в вашей памяти.4
Модуль os пытается также предоставить переносимый интерфейс программирования для используемой операционной системы: его функции могут быть по-разному реализованы на различных платформах, но для сценариев на языке Python они выглядят одинаково. Кроме того, модуль os экспортирует вложенный подмодуль os.path, предоставляющий переносимый интерфейс к средствам обработки файлов и каталогов.
Источники документации по модулям
Как можно заключить из предыдущих абзацев, обучение системному программированию на языке Python в основном сводится к изучению системных модулей Python. К счастью, существует множество источников информации, облегчающих эту задачу, - от атрибутов модуля до печатных справочников и книг.
Например, если потребуется узнать, какие элементы экспортирует встроенный модуль, можно прочесть соответствующий раздел руководства по библиотеке, исследовать его исходный код (все-таки Python является открытым программным обеспечением) или получить список атрибутов и строку документации в интерактивном режиме. Давайте импортируем модуль sys в Python 3.1 и посмотрим, что в нем находится:
C:\...\PP4E\System> python >>> import sys >>> dir(sys)
[‘__displayhook__’, ‘__doc__’, ‘__excepthook__’, ‘__name__’, ‘__package__’,
‘__stderr__’, ‘__stdin__’, ‘__stdout__’, ‘_clear_type_cache’, ‘_current_
frames’, ‘_getframe’, ‘api_version’, ‘argv’, ‘builtin_module_names’, ‘byteorder’,’call_tracing’, ‘callstats’, ‘copyright’, ‘displayhook’,
‘dllhandle’, ‘dont_write_bytecode’, ‘exc_info’, ‘excepthook’, ‘exec_ prefix’, ‘executable’, ‘exit’, ‘flags’, ‘float_info’, ‘float_repr_style’, ‘getcheckinterval’, ‘getdefaultencoding’, ‘getfilesystemencoding’,
‘getprofile’, ‘getrecursionlimit’, ‘getrefcount’, ‘getsizeof’, ‘gettrace’, ‘getwindowsversion’, ‘hexversion’, ‘int_info’, ‘intern’, ‘maxsize’,
‘maxunicode’, ‘meta_path’, ‘modules’, ‘path’, ‘path_hooks’, ‘path_importer_ cache’, ‘platform’, ‘prefix’, ‘ps1’, ‘ps2’,
‘setcheckinterval’, ‘setfilesystemencoding’, ‘setprofile’, ‘setrecursionlimit’, ‘settrace’, ‘stderr’, ‘stdin’, ‘stdout’, ‘subversion’, ‘version’, ‘version_ info’, ‘warnoptions’, ‘winver’]
Функция dir просто возвращает список строк с именами всех атрибутов для любого объекта, имеющего атрибуты; это удобная подсказка по содержимому модуля при работе в интерактивном режиме. Мы можем понять, например, что существует нечто с именем sys.version, поскольку имя version присутствует в списке, возвращаемом функцией dir. Если этого недостаточно, всегда можно обратиться к строке __doc__ встроенного модуля:
>>> sys.__doc__
"This module provides access to some objects used or maintained by the\ ninterpreter and to functions that interact strongly with the interpreter.\n\ nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known \npath -- module search path; path[0] is the script directory, else ‘’\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an i ...далее следует еще много текста... ”
Постраничный вывод строк документации
Встроенный атрибут __doc__ обычно содержит строку документации, которая может выглядеть несколько странно при отображении в таком виде, - это одна длинная строка с символами перевода строки, выводящимися как \n, а не красивый список строк. Чтобы отформатировать эти строки и придать им более удобочитаемый вид, можно воспользоваться функцией print:
>>> print(sys.__doc__)
This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter.
Dynamic objects:
argv -- command line arguments; argv[0] is the script pathname if known path -- module search path; path[0] is the script directory, else ‘’ modules -- dictionary of loaded modules
...далее следует еще много текста...
Встроенная функция print, в отличие от механизма вывода в интерактивной оболочке, корректно интерпретирует символы перевода строки. К сожалению, функция print не реализует возможность прокрутки или постраничного просмотра и поэтому может быть неудобной на некоторых платформах. Большего успеха можно добиться с помощью других инструментов, таких как встроенная функция help:
>>> help(sys)
Help on built-in module sys:
NAME
sys
FILE
(built-in)
MODULE DOCS
http://docs.python.org/library/sys
DESCRIPTION
This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter.
Dynamic objects:
argv -- command line arguments; argv[0] is the script pathname if known path -- module search path; path[0] is the script directory, else ‘’ modules -- dictionary of loaded modules
...далее следует еще много текста...
Функция help - это один из интерфейсов, предоставляемых системой PyDoc. Она входит в состав стандартной библиотеки, распространяемой вместе с Python, и предназначена для отображения в форматированном виде документации (строк документации, а также дополнительной структурной информации), связанной с объектом. Документация может быть в формате страниц справочного руководства Unix, который используется для вывода с помощью функции help, или в виде HTML-страницы, что еще лучше. Это очень удобный способ получения начальной информации при работе в интерактивном режиме, и это последний шанс разобраться, прежде чем погрузиться в справочники и книги.
Сценарий постраничного вывода
Функция help, с которой мы только что познакомились, также не обладает достаточной гибкостью при отображении информации. Хотя она и пытается в некоторых ситуациях обеспечить постраничный вывод, тем не менее на некоторых компьютерах - из тех, на которых мне приходилось работать, - она неточно выбирает размер страницы. Кроме того, она вообще не обеспечивает постраничный просмотр в графическом интерфейсе IDLE; вместо этого предлагается использовать полосу прокрутки, что весьма неудобно на больших мониторах. Когда мне требуется получить более полный контроль над тем, как функция help будет выводить текст, я обычно использую свой собственный вспомогательный сценарий, представленный в примере 2.1.
Пример 2.1. PP4E\System\more.py
разбивает строку или текстовый файл на страницы для интерактивного просмотра def more(text, numlines=15):
lines = text.splitlines() # подобно split(‘\n’) но без ‘’ в конце
while lines:
chunk = lines[:numlines] lines = lines[numlines:] for line in chunk: print(line)
if lines and input(‘More?’) not in [‘y’, ‘Y’]: break if __name__ == ‘__main__’:
import sys # если запускается как сценарий
more(open(sys.argv[1]).read(), 10) # отобразить постранично содержимое
# файла, указанного в командной строке
Главной в этом файле является функция more, и если вы обладаете достаточными знаниями языка Python, чтобы читать эту книгу, вы без труда поймете ее. Она просто разбивает строку по символам перевода строки, а затем извлекается срез и выводится сразу несколько строк (по умолчанию 15), чтобы избежать прокрутки экрана. Выражение извлечения среза lines[:15] вернет первые 15 элементов списка, a выражение lines[15:] - последние. Чтобы изменить размер страницы, передайте требуемое число строк в аргументе numlines (например, в последней строке примера 2.1 в аргументе numlines функции more передается число 10).
Вызов строкового метода splitlines, используемый в этом сценарии, возвращает список подстрок, полученный в результате разбиения исходной строки по символам перевода строки (например, [“line”, “line”,...]). Альтернативный метод split позволяет получить похожий результат, но в последнем элементе массива он возвращает пустую строку, если исходная строка заканчивается символом \n:
>>> line = 'aaa\nbbb\nccc\n'
>>> line.split('\n')
[‘aaa’, ‘bbb’, ‘ccc’, ‘’]
>>> line.splitlines()
[‘aaa’, ‘bbb’, ‘ccc’]
Как будет показано далее в главе 4, символом конца строки в сценариях на языке Python всегда является \n (обозначающий байт с числовым значением 10), вне зависимости от платформы. (Если вы еще не знаете, почему это имеет значение, - символы DOS \r отбрасываются при чтении.)
Основы использования строковых методов
Пример 2.1 - это простая программа на языке Python, но в ней представлены три важные темы, заслуживающие краткого упоминания: она использует строковые методы, осуществляет чтение из файла и предусматривает возможность импортирования или выполнения как самостоятельного сценария. Строковые методы в языке Python как таковые не являются системными инструментами, но их можно встретить в большинстве программ на языке Python. В действительности они будут постоянно встречаться на протяжении этой и последующих глав, поэтому коротко рассмотрим наиболее полезные инструменты, имеющиеся в наборе. В число строковых методов входят вызовы для поиска и замены:
>>> mystr = 'xxxSPAMxxx'
>>> mystr.find('SPAM') # вернет индекс первого вхождения
3
>>> mystr = 'xxaaxxaa'
>>> mystr.replace('aa', 'SPAM') # замена всех вхождений
‘xxSPAMxxSPAM’
Вызов метода find возвращает смещение первого вхождения подстроки, а метод replace осуществляет глобальный поиск и замену. Как и все строковые операции, метод replace возвращает новую строку, оставляя исходную строку неизменной (напомню, что строки являются неизменяемыми объектами). Для всех этих методов подстроки являются просто строками; в главе 19 будет представлен модуль re, который позволяет использовать шаблоны регулярных выражений при поиске и замене.
В самых последних версиях Python имеется возможность использовать оператор in определения принадлежности, как альтернативу методу find, когда необходимо всего лишь получить ответ «да» или «нет» (он проверяет присутствие подстроки в строке). Существует также несколько методов, удаляющих пробельные символы из концов строки, что особенно полезно при работе с текстовыми строками, извлекаемыми из файла:
>>> mystr = 'xxxSPAMxxx'
>>> 'SPAM' in mystr
# проверка присутствия подстроки в строке
True
>>> 'Ni' in mystr
# если подстрока отсутствует
False
>>> mystr.find('Ni')
-1
>>> mystr = '\t Ni\n'
>>> mystr.strip() # удалит пробельные символы
‘Ni’
>>> mystr.rstrip() # то же самое, но только с правого конца
‘\t Ni’
Среди строковых методов имеются также функции, которые могут быть полезны, например, как инструменты преобразования регистра символов, а кроме того, в стандартной библиотеке имеется модуль string, определяющий некоторые полезные константы:
>>> mystr = 'SHRUBBERY'
>>> mystr.lower() # преобразует регистр символов
‘shrubbery’
>>> mystr.isalpha() # проверяет содержимое
True
>>> mystr.isdigit()
False
>>> import string # константы, например, для использования в ‘in’
>>> string.ascii_lowercase
‘abcdefghijklmnopqrstuvwxyz’
>>> string.whitespace # пробельные символы
‘ \t\n\r\x0b\x0c’
Существуют также методы, позволяющие разбивать строки по подстрокам-разделителям и объединять их вместе, вставляя между ними подстроку. Эти средства будут изучены далее в этой книге, но в качестве знакомства покажем, как они работают:
>>> mystr = 'aaa,bbb,ccc'
>>> mystr.split(',') # разбить в список подстрок
[‘aaa’, ‘bbb’, ‘ccc’] >>> mystr = 'a b\nc\nd'
>>> mystr.split() # разделитель по умолчанию: пробельные символы
[‘a’, ‘b’, ‘c’, ‘d’]
>>> delim = 'NI'
>>> delim.join(['aaa', 'bbb', 'ccc']) # объединить подстроки из списка
‘aaaNIbbbNIccc’
>>> ' '.join(['A', 'dead', 'parrot']) # добавить пробел между подстроками ‘A dead parrot’
>>> chars = list('Lorreta') # преобразовать в список символов >>> chars
[‘L’, ‘o’, ‘r’, ‘r’, ‘e’, ‘t’, ‘a’]
>>> chars.append('!')
>>> ''.join(chars) # преобразовать в строку: с пустым разделителем
‘Lorreta!’
Эти вызовы оказываются удивительно мощными. Например, строку с колонками данных, разделенными символами табуляции, можно разобрать по колонкам единственным вызовом метода split; сценарий more.py, представленный выше, использует разновидность splitlines этого метода, чтобы разбить строку в список строк. На практике вызов метода replace можно эмулировать с помощью комбинации split/join:
>>> mystr = 'xxaaxxaa'
>>> 'SPAM'.join(mystr.split('aa')) # усложненная версия str.replace! ‘xxSPAMxxSPAM’
Запомните на будущее, что язык Python не предусматривает автоматического преобразования строк в числа и обратно, поэтому если в этом возникнет необходимость, такие преобразования необходимо выполнять явно:
>>> int("42"), eval("42") # преобразование строки в целое число
(42, 42)
>>> str(42), repr(42) # преобразование целого числа в строку
(‘42’, ‘42’)
>>> ("%d" % 42), '{:d}'.format(42) # с помощью оператора и метода форматиров. (‘42’, ‘42’)
>>> "42” + str(1), int("42") + 1 # в операциях конкатенации и сложения
(‘421’, 43)
В последней приведенной инструкции первое выражение выполняет конкатенацию строк (так как оба операнда являются строками), а второе выполняет сложение целых чисел (поскольку оба объекта являются числами). Python не делает предположений о том, какое преобразование вы могли иметь в виду, и не выполняет преобразования автоматически. Одно из главных правил интерпретатора Python - где только возможно, избегать закулисных магических действий и попыток что-то угадывать. Более подробно о средствах для работы со строками будет рассказано далее (им посвящена целая глава в пятой части), но, кроме того, стоит посмотреть описание дополнительных строковых инструментов в руководстве по библиотеке.
Другие особенности строк в Python 3.X:
Юникод и тип bytes
Строго говоря, история со строками в Python 3.X гораздо богаче, чем можно было бы заключить из вышесказанного. До сих пор было продемонстрировано, что объекты типа str являются последовательностями символов (точнее - «кодовыми пунктами» Юникода, представляющими «элементы» Юникода), которые могут быть не только символами ASCII, но и многобайтовыми символами Юникода и предусматривают возможность кодирования и декодирования вручную или автоматически при выполнении операций с текстовыми файлами. Строки в программном коде заключаются в кавычки (например, abc) и допускают использование дополнительного синтаксиса для представления символов, не входящих в набор ASCII (например, ‘\xc4\xe8’, ‘\u00c4\u00e8’).
Однако на самом деле в Python 3.X имеется два дополнительных строковых типа, поддерживающих большинство операций, которыми обладает тип str: тип bytes - последовательность коротких целых чисел для представления 8-битовых двоичных данных и тип bytearray - изменяемый вариант типа bytes. Как вы уже знаете, присутствие символа «Ь» перед открывающей кавычкой (например, b’abc’, b’\xc4\xe8’) говорит о том, что вы имеете дело с объектом типа bytes. Как будет показано в главе 4, файлы в Python 3.X также проявляют подобную двойственность: при работе в текстовом режиме используется тип str (при этом предусматриваются преобразования символов конца строки и символов Юникода, в соответствии с указанной кодировкой), а при работе в двоичном режиме используется тип bytes (в этом случае данные при чтении/записи не подвергаются преобразованиям). В главе 5 мы увидим такое же деление при работе с такими инструментами, как сокеты, которые на сегодняшний день работают со строками байтов
Текст Юникода используется в интернационализированных приложениях, и многие инструменты языка Python, ранее ориентированные на работу с двоичными данными, в настоящее время работают со строками байтов. К ним относятся некоторые инструменты для работы с файлами, которые мы встретим далее, такие как функция open, а также инструменты os.listdir и os.walk, которые мы будем изучать в последующих главах. Как будет показано ниже, даже простые инструменты для работы с каталогами должны иметь возможность обрабатывать символы Юникода в содержимом и в именах файлов. Кроме того, инструменты для сериализации объектов и анализа двоичных данных на сегодняшний день ориентированы на работу со строками байтов.
Далее в этой книге мы также увидим, что в настоящее время Юникод используется для представления текста в графических интерфейсах; для обмена данными по сети, в виде последовательностей байтов; в стандартных инструментах Интернета, таких как электронная почта; и даже в некоторых механизмах сохранения объектов, таких как файлы DBM и модуль shelve. Любой интерфейс, предусматривающий работу с текстом, на сегодняшний день обязательно предусматривает работу с Юникодом, потому что тип str представляет строки символов Юникода, а не только ASCII. Как только мы в этой книге доберемся до сферы программирования приложений, для большинства программистов на Python 3.X тема Юникода перестанет быть необязательной.
Мы отложим дальнейшее обсуждение Юникода, пока нам не представится возможность увидеть его в прикладном контексте и в практических программах. Более фундаментальное освещение поддержки текстовых и двоичных данных Юникода в строках и файлах вы найдете в четвертом издании книги «Изучаем Python». Эта книга официально посвящена основам языка, что предполагает углубленное рассмотрение тем и позволило отвести этой теме отдельную главу, занимающую 45 страниц.
Основы операций с файлами
Помимо обработки строк, сценарий more.py также использует файлы - он открывает внешний файл, имя которого задается в командной строке, с помощью встроенной функции open, и целиком считывает его в память с помощью метода read объекта файла. Поскольку объекты файлов, возвращаемые функцией open, являются составной частью самого базового языка Python, я могу предположить, что вы хотя бы бегло знакомы с ними. Но в случае, если эта глава попалась вам в самом начале изучения Python, подскажу, что следующие вызовы загружают в строку содержимое файла, загружают в строку набор байтов фиксированной длины, загружают содержимое файла в список строк и загружают в строку очередную строку файла, соответственно:
open(‘file’).read() # читает весь файл в строку
open(‘file’).read(N) # читает следующие N байтов в строку
open(‘file’).readlines() # читает весь файл в массив строк
open(‘file’).readline() # читает следующую строку, до символа ‘\n’
Как мы скоро увидим, эти вызовы можно также применять в Python к командам оболочки, чтобы прочитать их вывод. У объектов файлов есть также методы write, которые посылают строки в соответствующий файл. Более глубоко темы, связанные с файлами, раскрываются в главе 4, однако сами операции вывода данных в файл и чтения их обратно в языке Python реализуются очень просто:
>>> file = open('spam.txt', 'w') # создать файл spam.txt
>>> file.write(('spam' * 5) + '\n') # записать текст: вернет
21 # число записанных символов >>> file.close()
>>> file = open('spam.txt') # или open(‘spam.txt’).read()
>>> text = file.read() # прочитать в строку
>>> text
‘spamspamspamspamspam\n’
Два способа использования программ
Последние несколько строк в сценарии more.py из примера 2.1 знакомят нас с одним из первых важных понятий в программировании инструментов командной строки. Они настраивают файл так, чтобы его можно было использовать двумя способами: как сценарий или как библиотеку.
Напомню, что в каждом модуле Python доступна встроенная переменная__name__, в которую интерпретатор Python записывает значение
__main__, только если файл выполняется как программа, а не импортируется в качестве библиотеки. Благодаря этому функция more в этом файле автоматически выполняется в последней строке файла, когда сценарий запускается, как самостоятельная программа, а не импортируется в какое-либо другое место. Этот простой прием является ключом к созданию многократно используемых сценариев: благодаря реализации логики программы в виде функции, а не в виде программного кода верхнего уровня, ее можно импортировать и повторно использовать в других сценариях.
В результате появляется возможность запускать more.py отдельно или импортировать его и вызывать функцию more из любого другого места. При запуске файла как самостоятельной программы мы передаем ей в командной строке имя файла, который нужно прочесть и выводить постранично: в следующей главе будет полностью описано, как слова, вводимые в команде для запуска программы, появляются во встроенном списке sys.argv. Ниже приводится пример запуска файла сценария для постраничного вывода самого себя (эта команда должна выполняться в каталоге PP4E\System, иначе входной файл не будет найден; причина этого будет пояснена позднее):
C:\...\PP4E\System> python more.py more.py
разбивает строку или текстовый файл на страницы для интерактивного просмотра def more(text, numlines=15):
lines = text.splitlines() # подобно split(‘\n’), но без ‘’ в конце
while lines:
chunk = lines[:numlines] lines = lines[numlines:] for line in chunk: print(line)
More?y
if lines and input(‘More?’) not in [‘y’, ‘Y’]: break
if__name__== ‘__main__’:
import sys # если запускается как сценарий
more(open(sys.argv[1]).read(), 10) # отобразить постранично содержимое
# файла, указанного в командной строке
Если мы импортируем файл more.py, мы явно передаем строку в его функцию more; функция more - как раз такая утилита, которая нам нужна для просмотра текста документации. Запуск этой утилиты для просмотра строки документации модуля sys представит информацию о том, какие возможности дает этот модуль сценариям, в виде, пригодном для чтения:
C:\...\PP4E\System> python >>> from more import more >>> import sys >>> more(sys.__doc__)
This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter.
Dynamic objects:
argv -- command line arguments; argv[0] is the script pathname if known path -- module search path; path[0] is the script directory, else ‘’ modules -- dictionary of loaded modules
displayhook -- called to show results in an interactive session excepthook -- called to handle any uncaught exception other than SystemExit To customize printing in an interactive session or to install a custom top-level exception handler, assign other functions to replace these.
stdin -- standard input file object; used by input()
More?
Нажатие клавиши у или Y заставит функцию отобразить несколько следующих строк документации и снова вывести приглашение, если список строк еще не закончился. Попробуйте сделать это у себя, и вы увидите, как выглядит оставшаяся часть строки документации. Кроме того, попробуйте поэкспериментировать, задавая размер окна во втором аргументе, - вызов more(sys.__doc__, 5) будет выводить текст блока
ми по 5 строк.
Руководства по библиотекам Python
Если изложение кажется недостаточно детальным, то полную информацию вы можете получить, обратившись к разделу, посвященному модулю sys, в руководстве по библиотекам Python. Все стандартные руководства Python доступны в Интернете и, кроме того, часто устанавливаются вместе с Python. В Windows стандартные руководства устанавливаются автоматически. Для обращения к руководствам приведу несколько простых указаний:
• В Windows щелкните на кнопке Пуск (Start), выберите пункт Все программы (ALL Programs), затем выберите пункт Python и пункт Python ManuaLs (Руководства Python). Руководства чудесным образом появятся на вашем экране. Начиная с версии Python 2.4 руководства для Windows поставляются в формате файлов справки, благодаря чему они поддерживают возможность поиска и навигации.
• В Linux или Mac OS X можно щелкнуть на элементе руководства в менеджере файлов или запустить броузер из командной строки и перейти в каталог, где в вашей системе находятся файлы HTML руководства.
• Если в вашей системе руководств не обнаружилось, их всегда можно прочесть в Интернете. Перейдите на веб-сайт Python http://www. python.org и найдите ссылки, ведущие к документации. Этот сайт также обеспечивает возможность простого поиска поруководствам.
В любом случае выберите руководство «Library», если вас интересуют такие вещи, как модуль sys. Это руководство содержит описание всех стандартных модулей, встроенных типов данных и функций и многое другое. В комплект стандартных руководств Python входит также краткий учебник, справочник по языку, справочники по расширениям и многое другое.
Коммерческие справочники
Рискуя заслужить упрек за рекламу в книге, я должен упомянуть, что можно приобрести комплект руководств по Python, отпечатанный и переплетенный; подробности и ссылки можно найти на информационной странице по изданиям на сайте http://www.python.org . На сегодняшний день есть также коммерческие печатные справочники по Python, в том числе «Python Essential Reference»5 , «Python in a Nutshell», «Python Standard Library» и «Python Pocket Reference». Некоторые из этих книг являются более полными и содержат примеры, при этом последний из перечисленных справочников удобно использовать как «напоминал-ку», после того как вы уже раз-другой изучили библиотеку.6
Модуль sys
Но достаточно разговоров об источниках информации (и основах разработки сценариев) - перейдем к подробностям, касающихся системных модулей. Как говорилось выше, модули sys и os образуют ядро набора инструментов Python для решения системных задач. Сделаем сейчас краткий интерактивный обзор некоторых инструментов, имеющихся в этих двух модулях, прежде чем использовать их в более крупных примерах. Начнем с модуля sys, меньшего из этих двух модулей. Напомню, чтобы получить полный список всех атрибутов модуля sys, вы можете передать его функции dir (или посмотреть на список, полученный нами выше в этой главе).
Платформы и версии
Как и в большинстве модулей, в модуле sys есть атрибуты, содержащие информацию, и функции, выполняющие действия. Например, в его атрибутах можно найти название операционной системы, в которой выполняется программный код, наибольшее целое число, поддерживаемое аппаратной платформой на данном компьютере (хотя в Python 3.X целые числа могут быть произвольной величины), и номер версии интерпретатора Python, выполняющего программный код:
C:\...\PP4E\System> python
>>> import sys
>>> sys.platform, sys.maxsize, sys.version
(‘win32’, 2147483647, ‘3.1.1 (r311:74483, Aug 17 2009, 17:02:12)
...дополнительные строки были удалены... ’)
>>> if sys.platform[:3] == 'win': print('hello windows')
hello windows
Если программный код должен по-разному выполняться на разных компьютерах, просто проверьте строку sys.platform, как сделано в этом примере. Несмотря на то, что Python по большей части независим от платформы, а непереносимые средства обычно заключаются в условные инструкции if, типа той, что здесь приведена, тем не менее далее, например, будет показано, что средства запуска программ и низкоуровневые взаимодействия с консолью различаются в зависимости от платформы. Поэтому проверка значения sys.platform поможет выбрать правильный инструмент для той машины, на которой выполняется сценарий.
Путь поиска модулей
Модуль sys позволяет также проверить путь поиска модулей как в интерактивном режиме, так и из программы на языке Python. Переменная sys.path хранит список строк, представляющих действительный путь поиска в выполняющемся интерпретаторе Python. Когда выполняется операция импортирования модуля, Python просматривает этот путь слева направо, пытаясь отыскать файл модуля в каждом каталоге, указанном в списке. Поэтому данная переменная позволит вам убедиться, что путь поиска действительно задан так, как нужно.7
Список sys.path просто инициализируется при первом запуске интерпретатора из PYTHONPATH с добавлением системных значений по умолчанию и содержимого файлов .pth , находящихся в каталогах со сценариями. В действительности, если заглянуть в список sys.path в интерактивной оболочке, можно обнаружить довольно много каталогов, которые отсутствуют в переменной PYTHONPATH: в него входит также указатель на домашний каталог сценария (пустая строка - назначение которой я объясню далее, после знакомства с функцией os.getcwd) и набор каталогов стандартных библиотек, который может быть различным в каждой установке:
>>> sys.path
[‘’, ‘C:\\PP4thEd\\Examples’, ...плюс каталоги стандартной библиотеки... ]
Как это ни удивительно, но список sys.path можно изменять программным способом. Сценарии могут использовать такие операции над списками, как append, extend, insert, pop и remove, а также использовать инструкцию del, чтобы изменять путь поиска модулей в процессе выполнения, чтобы подключить все каталоги с необходимыми модулями. Python всегда использует для импорта текущее значение sys.path, учитывая все внесенные вами изменения:
>>> sys.path.append(r'C:\mydir')
>>> sys.path
[‘’, ‘C:\\PP4thEd\\Examples’, ...more deleted..., ‘C:\\mydir’]
Такое непосредственное изменение переменной sys.path является альтернативой установке переменной оболочки PYTHONPATH, хотя и не самой лучшей. Изменения в sys.path сохраняются лишь до завершения процесса Python, и их нужно повторно вносить при каждом новом запуске программы или сеанса Python. Однако некоторые типы программ (например, сценарии, выполняющиеся на веб-сервере) не должны зависеть от значения PYTHONPATH. Такие сценарии могут сами настраивать список sys.path при запуске и включать в него все необходимые каталоги с импортируемыми модулями. Более конкретный пример использования sys.path приводится в примере 1.34, в предыдущей главе, где мы вынуждены были предусмотреть динамическую настройку пути поиска, так как применение веб-сервера не позволяет делать какие-либо предположения о путях к импортируемым модулям.
Пути к каталогам в Windows
Обратите внимание, что при настройке списка sys.path в примерах выше были использованы литералы необрабатываемых строк (raw string): поскольку обратный слеш в строке Python обычно означает начало экранированной последовательности, пользователи Windows должны следить за тем, чтобы удваивать символы слеша при использовании в строках с путями к каталогам (например, в строке "C:\\dir" комбинация \\ в действительности является экранированной последовательностью, означающей символ \), или использовать константы необрабатываемых строк, чтобы иметь возможность вставлять символ обратного слеша без всяких ухищрений (например, r"C:\dir").
При просмотре путей к каталогам в Windows (например, при выводе списка sys.path в интерактивной оболочке) Python выводит \\, как один символ \. Формально можно обойтись одним символом \, если за ним следует символ, не воспринимаемый Python как продолжение экранированной последовательности, но использовать удвоение и необрабатываемые строки обычно легче, чем запоминать таблицы экранированных последовательностей.
Обратите также внимание, что большинство библиотечных функций Python в качестве разделителей элементов путей к каталогам принимают как прямой (/), так и обратный (\) слеш, независимо от используемой платформы. Это значит, что / обычно действует и в Windows, что способствует созданию сценариев, переносимых на Unix. Описываемые далее в этой главе инструменты из модулей os и os.path также способствуют переносимости путей в сценариях.
Таблица загруженных модулей
В модуле sys есть также средства, позволяющие подключиться к интерпретатору. Например, переменная sys.modules служит словарем, содержащим записи вида namemodule для каждого модуля, импортированного в сеанс или программу Python (точнее, в вызывающий процесс Python):
>>> sys.modules
{‘reprlib’: ,
...часть строк удалена...
>>> list(sys.modules.keys())
[‘reprlib’, ‘heapq’, ‘__future__’, ‘sre_compile’, ‘_collections’, ‘locale’,
‘_sre’, ‘functools’, ‘encodings’, ‘site’, ‘operator’, ‘io’, ‘__main__’, ...часть
строк удалена... ]
>>> sys
>>> sys.modules['sys']
Мы могли бы использовать эту переменную для создания программ, выводящих или иным образом обрабатывающих все модули, загруженные программой (нужно просто обойти в цикле список ключей sys.modules).
Аналогичным средством подключения к интерпретатору является счетчик ссылок на объекты, доступный через переменную sys.getrefcount, и список имен модулей, встроенных в выполняемый файл интерпретатора Python (sys.builtin_module_names). Более подробные сведения вы найдете в руководстве по библиотеке Python. Подобные переменные главным образом предназначены для получения внутренней информации интерпретатора Python, но иногда они могут иметь большое значение для программистов, создающих инструменты для других программистов.
Сведения об исключениях
Некоторые атрибуты модуля sys позволяют получить все сведения о самом последнем исключении, возбужденном интерпретатором Python. Это удобно, когда требуется реализовать обобщенную процедуру обработки исключений. Например, функция sys.exc_info возвращает кортеж, содержащий тип последнего исключения, его значение и объект с трассировочной информацией. В модели исключений, которая в Python 3 полностью основана на классах, первые два элемента кортежа соответствуют классу последнего исключения и его экземпляру:
>>> try:
... raise IndexError
... except:
... print(sys.exc_info())
(, IndexError(), )
Эту информацию можно использовать для создания собственного сообщения об ошибке, выводимого в окне диалога графического интерфейса или в веб-странице HTML (напомню, что не обработанные исключения по умолчанию завершают программы с выводом сообщения об ошибке). Первые два элемента кортежа, возвращаемого этой функцией, по умолчанию предусматривают вывод полезной информации, а третий элемент, объект с трассировочной информацией, можно обработать с помощью стандартного модуля traceback:
>>> import traceback, sys >>> def grail(x):
... raise TypeError('already got one')
>>> try:
... grail('arthur')
... except:
... exc_info = sys.exc_info()
... print(exc_info[0])
... print(exc_info[1])
... traceback.print_tb(exc_info[2])
already got one
File "", line 2, in
File "", line 2, in grail
Модуль traceback может также представлять сообщения в виде строк и записывать их в указанный объект файла - более подробную информацию вы найдете в руководстве по библиотеке Python.
Другие элементы, экспортируемые модулем sys
Модуль sys экспортирует и другие часто используемые инструменты, с которыми мы встретимся в контексте более крупных тем и примеров далее в этой части книги. Например:
• Аргументы командной строки можно получить в виде списка строк под именем sys.argv
• Стандартные потоки ввода-вывода доступны в виде sys.stdin, sys. stdout и sys.stderr
• Завершение программы можно вызвать с помощью функции sys. exit
Однако поскольку все эти инструменты ведут к более крупным темам, о них будет рассказано ниже в отдельных разделах.
Модуль os
Как уже говорилось выше, модуль os - более крупный из двух основных системных модулей. В нем содержатся все обычные вызовы операционной системы, с которыми вы могли ранее встречаться в своих программах на языке C и в сценариях оболочки. Его вызовы имеют дело с каталогами, процессами, переменными оболочки и так далее. Формально этот модуль предоставляет инструментальные средства POSIX - переносимого стандарта вызовов операционной системы - вместе с платформонезависимыми средствами работы с каталогами, к которым относится вложенный модуль os.path. Функционально модуль os играет роль переносимого интерфейса к системным вызовам операционной системы: сценарии, написанные с использованием модулей os и os.path, обычно могут выполняться на любой платформе без внесения изменений. На некоторых платформах модуль os включает дополнительные инструменты, доступные только на этой платформе (например, низкоуровневые операции с процессами в Unix). Однако, в общем и целом, этот модуль является кросс-платформенным, насколько это технически возможно.
Инструменты в модуле os
Бросим беглый взгляд на основные интерфейсы в модуле os. В табл. 2.1 приводится список наиболее часто используемых инструментов из модуля os, организованных по функциональному назначению.
Таблица 2.1. Часто используемые инструменты из модуля os
Область применения
Инструменты
Переменные окружения
os.environ
Запуск программ
os.system, os.popen, os.execv, os.spawnv
Порождение дочерних процессов
os.fork, os.pipe, os.waitpid, os.kill
Дескрипторы файлов, блокировки
os.open, os.read, os.write
Обработка файлов
os.remove, os.rename, os.mkfifo, os.mkdir, os. rmdir
Инструменты администрирования
os.getcwd, os.chdir, os.chmod, os.getpid, os.listdir, os.access
Инструменты обеспечения переносимости
os.sep, os.pathsep, os.curdir, os.path.split, os.path.join
Инструменты для работы с путями к каталогам
os.path.exists(‘path’), os.path.isdir(‘path’), o s.p at h.g et s i z e (‘p a t h’)
Если попробовать получить перечень атрибутов этого модуля в интерактивном режиме, получится громадный список имен, который будет различным для разных версий Python. Скорее всего, он будет зависеть от платформы и не будет слишком полезен, если не изучить, что означает каждое имя (я позволил себе немного отформатировать этот список и удалить часть строк для экономии места - запустите эту команду у себя):
>>> import os >>> dir(os)
[‘F_OK’, ‘MutableMapping’, ‘O_APPEND’, ‘O_BINARY’, ‘O_CREAT’, ‘O_EXCL’, ‘O_ NOINHERIT’, ‘O_RANDOM’, ‘O_RDONLY’, ‘ O_RDWR’, ‘ O_SEQUENTIAL’, ‘ O_SHORT_LIVED’,
‘O_TEMPORARY’, ‘O_TEXT’, ‘O_TRUNC’, ‘O_WRONLY’, ‘P_DETACH’, ‘P_NOWAIT’, ‘P_ NOWAITO’, ‘P_OVERLAY’, ‘P_WAIT’, ‘ R_OK’, ‘ SEEK_CUR’, ‘ SEEK_END’, ‘ SEEK_SET’, ‘TMP_MAX’,
...здесь было удалено 9 строк...
‘pardir’, ‘path’, ‘pathsep’, ‘pipe’, ‘popen’, ‘putenv’, ‘read’, ‘remove’, ‘rem ovedirs’, ‘rename’, ‘renames’, ‘rmdir’, ‘sep’, ‘spawnl’, ‘spawnle’, ‘spawnv’, ‘spawnve’, ‘startfile’, ‘stat’, ‘stat_float_times’, ‘stat_result’, ‘statvfs_ result’, ‘strerror’, ‘sys’, ‘system’, ‘times’, ‘umask’, ‘unlink’, ‘urandom’, ‘utime’, ‘waitpid’, ‘walk’, ‘write’]
Помимо этого вложенный модуль os.path экспортирует дополнительные инструменты, большинство из которых связано с обеспечением переносимости при выполнении операций с именами файлов и каталогов:
>>> dir(os.path)
[‘__all__’, ‘__builtins__’, ‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’, ‘_get_altsep’, ‘_get_bothseps’, ‘_get_colon’, ‘_get_dot’, ‘_get_empty’, ‘_ get_sep’, ‘_getfullpathname’, ‘abspath’, ‘altsep’, ‘basename’, ‘commonprefix’, ‘curdir’, ‘defpath’, ‘devnull’, ‘dirname’, ‘exists’, ‘expanduser’, ‘expandvars’, ‘extsep’, ‘genericpath’, ‘getatime’, ‘getctime’, ‘getmtime’, ‘getsize’,
‘isabs’, ‘isdir’, ‘isfile’, ‘islink’, ‘ismount’, ‘join’, ‘lexists’, ‘normcase’, ‘normpath’, ‘os’, ‘pardir’, ‘pathsep’, ‘realpath’, ‘relpath’, ‘sep’, ‘split’, ‘splitdrive’, ‘splitext’, ‘splitunc’, ‘stat’, ‘supports_unicode_filenames’,
‘sys’]
Средства администрирования
Если вы полагаете, что приведения этих огромных листингов недостаточно, чтобы двинуться дальше, давайте поэкспериментируем с некоторыми из простейших инструментов модуля os в интерактивной оболочке. Как и модуль sys, модуль os содержит набор инструментов для получения информации и администрирования:
>>> os.getpid()
7980
>>> os.getcwd()
‘C:\\PP4thEd\\Examples\\PP4E\\System’
>>> os.chdir(r'C:\Users')
>>> os.getcwd()
‘C:\\Users’
Здесь видно, что функция os.getpid возвращает числовой идентификатор (ID) процесса (уникальный идентификатор выполняющейся программы, определяемый системой), а функция os.getcwd возвращает текущий рабочий каталог. Текущим рабочим каталогом является тот, в котором предполагается нахождение файлов, открываемых сценарием, если в их именах явно не указан путь к каталогу. Вот почему ранее я предлагал запустить следующую команду именно в том каталоге, где находится файл more.py:
C:\...\PP4E\System> python more.py more.py
Аргумент с именем входного файла здесь задан без явного пути к каталогу (хотя его можно указать, чтобы обеспечить постраничный вывод файлов из другого каталога). Если потребуется назначить текущим другой каталог, вызовите функцию os.chdir: ваш программный код будет выполняться относительно нового каталога до конца программы (или нового вызова os.chdir). В следующей главе еще будет говориться
о понятии текущего рабочего каталога и его связи с операцией импортирования модулей, когда мы будем изучать контекст выполнения сценария.
Константы переносимости
Модуль os экспортирует также ряд имен, упрощающих программирование для нескольких платформ. В этот набор входят специфические для платформ настройки символов-разделителей компонентов путей и каталогов, указателей на родительский и текущий каталоги и символов, используемых для завершения строк в используемой системе:
>>> os.pathsep, os.sep, os.pardir, os.curdir, os.linesep
(‘;’, ‘\\’, ‘..’, ‘.’, ‘\r\n’)
Константа os.sep определяет символ, который используется в качестве разделителя компонентов пути к каталогу на платформе, где выполняется Python. Она автоматически получает значение \ в Windows, / -в POSIX-совместимых системах и : - в некоторых версиях Mac. Аналогично константа os.pathsep определяет символ, отделяющий каталоги в списках каталогов. Она получает значение : в POSIX-совместимых системах и ; - в DOS и Windows.
Использование таких атрибутов для составления и разбора относящихся к системе строк делает сценарии полностью переносимыми. Например, вызов вида dirpath.split(os.sep) правильно разберет на составляющие специфические для платформы имена каталогов, даже если dirpath выглядит как dir\dir в Windows, dir/dir в Linux и dir:dir в некоторых версиях на Mac. Как уже говорилось выше, при определении имен открываемых файлов в Windows допускается использовать символы прямого слеша вместо символов обратного слеша, но применение этих констант обеспечивает независимость программного кода, реализующего операции с каталогами, от платформы, на которой он выполняется.
Обратите также внимание, что функция os.linesep в примере выше возвращает последовательность символов \r\n - экранированные последовательности, соответствующие комбинации символов возврата каретки и перевода строки, которая в Windows используется как признак конца строки и на которую обычно никто не обращает внимания при обработке текстовых файлов в Python. Подробнее о преобразовании символов конца строки будет рассказываться в главе 4.
Основные инструменты os.path
Вложенный модуль os.path предоставляет большой набор собственных средств для работы с каталогами. Например, в него входят переносимые функции для таких задач, как проверка типа файла (isdir, isfile и другие), подтверждение существования файла (exists) и получение размера файла по его имени (getsize):
>>> os.path.isdir(r'C:\Users'), os.path.isfile(r'C:\Users')
(True, False)
>>> os.path.isdir(r'C:\config.sys'), os.path.isfile(r'C:\config.sys')
(False, True)
>>> os.path.isdir('nonesuch'), os.path.isfile('nonesuch')
(False, False)
>>> os.path.exists(r'c:\Users\Brian')
False
>>> os.path.exists(r'c:\Users\Default')
True
>>> os.path.getsize(r'C:\autoexec.bat')
24
Функции os.path.isdir и os.path.isfile сообщают нам о том, является ли имя файла каталогом или простым файлом; обе они возвращают False, если указанный файл не существует (то есть отсутствие файла предполагает отрицание). Есть также функции для разбиения или объединения строк путей к каталогам, которые автоматически используют соглашения об именовании каталогов для той платформы, где работает Python:
>>> os.path.split(r'C:\temp\data.txt')
(‘C:\\temp’, ‘data.txt’)
>>> os.path.join(r'C:\temp', 'output.txt')
‘C:\\temp\\output.txt’
>>> name = r'C:\temp\data.txt' # пути в Windows
>>> os.path.dirname(name), os.path.basename(name)
(‘C:\\temp’, ‘data.txt’)
>>> name = '/home/lutz/temp/data.txt' # пути в стиле Unix
>>> os.path.dirname(name), os.path.basename(name)
(‘/home/lutz/temp’, ‘data.txt’)
>>> os.path.splitext(r'C:\PP4thEd\Examples\PP4E\PyDemos.pyw')
(‘C:\\PP4thEd\\Examples\\PP4E\\PyDemos’, ‘.pyw’)
Функция os.path.split отделяет имя файла от пути к его каталогу, a os.path.join снова соединяет их вместе, и все это - совершенно переносимым образом, с использованием соглашений по оформлению путей, действующих в той системе, где они вызываются. Функции dirname и base-name возвращают первый и второй элементы, возвращаемые функцией split, и реализованы просто для удобства, a функция splitext отделяет расширение файла (за последним символом ). Тонкое замечание: эти функции по своему действию почти эквивалентны строковым методам split и join, если вызывать их относительно строковой константы os.sep. Почти, но не совсем:
>>> os.sep
‘\\’
>>> pathname = r'C:\PP4thEd\Examples\PP4E\PyDemos.pyw'
>>> os.path.split(pathname) # отделить имя файла от каталога
(‘C:\\PP4thEd\\Examples\\PP4E’, ‘PyDemos.pyw’)
>>> pathname.split(os.sep) # разбить путь по символам слеша
[‘C:’, ‘PP4thEd’, ‘Examples’, ‘PP4E’, ‘PyDemos.pyw’]
>>> os.sep.join(pathname.split(os.sep))
‘C:\\PP4thEd\\Examples\\PP4E\\PyDemos.pyw’
>>> os.path.join(*pathname.split(os.sep))
‘C:PP4thEd\\Examples\\PP4E\\PyDemos.pyw’
Последний вызов join требует передачи отдельных аргументов (отсюда и символ *), но он не вставляет первый символ слеша после буквы, обозначающей имя диска в Windows. Если подобные отличия имеют большое значение, используйте предшествующий вызов метода str. join. Функция normpath может пригодиться в ситуациях, когда в путях произвольно смешиваются разделители компонентов пути для Unix и Windows:
>>> mixed
‘C:\\temp\\public/files/index.html’
>>> os.path.normpath(mixed)
‘C:\\temp\\public\\files\\index.html’
>>> print(os.path.normpath(r'C:\temp\\sub\.\file.ext'))
C:\temp\sub\file.ext
В этом модуле имеется также функция abspath, которая переносимым образом возвращает полное имя файла. Она учитывает добавленный текущий каталог, родительский каталог .. и многое другое:
>>> os.chdir(r'C:\Users')
>>> os.getcwd()
‘C:\\Users’
>>> os.path.abspath('') # пустая строка означает тек. раб. каталог (cwd) ‘C:\\Users’
>>> os.path.abspath('temp') # расширяет до полного пути к файлу в тек. кат. ‘C:\\Users\\temp’
>>> os.path.abspath(r'PP4E\dev') # частичный путь относительно тек. раб. кат. ‘C:\\Users\\PP4E\\dev’
>>> os.path.abspath('.') # расширяет относительные пути ‘C:\\Users’
>>> os.path.abspath('..')
‘C:\\’ >>> os.path.abspath(r'..\examples')
‘C:\\examples’
>>> os.path.abspath(r'C:\PP4thEd\chapters') # абсолютные пути не изменяются ‘C:\\PP4thEd\\chapters’
>>> os.path.abspath(r'C:\temp\spam.txt')
‘C:\YtempY\spam.txt’
Поскольку имена файлов считаются относящимися к текущему рабочему каталогу, если не заданы полными путями, функция os.path.ab-spath может пригодиться, если потребуется показать пользователю, какой каталог используется в действительности для сохранения файла. В Windows, например, при запуске программ с графическим интерфейсом щелчком на ярлыках в проводнике или на рабочем столе рабочим каталогом программы является тот, в котором находится запускаемый файл, что не всегда очевидно пользователю. В таких случаях может помочь вывод значения, возвращаемого функцией abspath для файла.
Выполнение команд оболочки из сценариев
Модуль os позволяет также выполнять команды оболочки из сценариев Python. Это понятие переплетается с другими, такими как потоки ввода-вывода, которые не будут освещаться в полной мере до следующей главы, но, поскольку это одно из ключевых понятий, которое будет использоваться на протяжении всей данной части книги, бегло коснемся основ. В модуле os имеются две функции, позволяющие запустить из сценария любую команду, которую можно ввести в окне консоли:
os.system
Запускает команду оболочки из сценария Python
os.popen
Запускает команду оболочки и соединяется с ее потоками ввода или вывода
Кроме того, существует относительно новый модуль subprocess, обеспечивающий более точное управление запускаемыми командами оболочки через потоки ввода-вывода, который может использоваться как альтернатива и даже для реализации двух функций, представленных выше (хотя и за счет некоторой сложности программного кода).
Что такое команда оболочки?
Чтобы понять область действия этих вызовов, нужно сначала ввести несколько терминов. В данной книге под термином оболочка (shell) подразумевается система, которая считывает и выполняет командные строки на вашем компьютере, а под командой оболочки (shell command) подразумевается командная строка, которую вы обычно вводите в ответ на приглашение оболочки.
В Windows, например, можно открыть окно консоли MS-DOS (она же Командная строка (Command Prompt)) и вводить в нем команды DOS, такие как dir для получения списка каталогов, type для просмотра файла, имена программ, которые нужно запустить, и так далее. DOS является системной оболочкой, а команды, такие как dir и type, - командами оболочки. В Linux и Mac OS X можно запустить новый сеанс оболочки, открыв окно терминала, и также вводить в него команды оболочки - ls для вывода списка каталогов, cat для просмотра файлов и так далее. Для Unix существует множество оболочек (например, csh, ksh), но все они читают и выполняют командные строки. Ниже показаны две команды оболочки, введенные и выполненные в окне консоли MS-DOS под Windows:
C:\...\PP4E\System> dir /B ...ввод команды оболочки helloshell.py ...далее следует вывод этой команды
more.py ...DOS играет роль оболочки в Windows
more.pyc spam.txt __init__.py
C:\...\PP4E\System> type helloshell.py
# a Python program print(‘The Meaning of Life’)
Выполнение команд оболочки
Конечно, все это не имеет прямого отношения к Python (несмотря на то, что сценарии командной строки на языке Python иногда ошибочно называют инструментами оболочки). Но, поскольку функции system и popen из модуля os позволяют сценариям Python выполнять любые команды, понятные оболочке системы, мы можем использовать в своих сценариях любые имеющиеся в системе инструменты командной строки, независимо от того, написаны они на Python или нет. Например, ниже приводится некоторый программный код на языке Python, который выполняет две команды оболочки DOS, введенные выше в ответ на приглашение оболочки:
C:\...\PP4E\System> python
>>> import os
>>> os.system('dir /B')
helloshell.py
more.py
more.pyc
spam.txt
__init__.py
0
>>> os.system('type helloshell.py')
# a Python program print(‘The Meaning of Life’)
0 >>> os.system('type hellshell.py')
The system cannot find the file specified.
1
Нули, которые выводятся по окончании выполнения первых двух команд, являются значениями, возвращаемыми самой функцией system. Функцию system можно использовать для выполнения любой командной строки, которую допускается ввести в ответ на приглашение оболочки (здесь приглашением является C:\...\PP4E\System>). Выводимые командой данные обычно попадают в стандартный поток вывода сеанса Python или программы.
Обмен данными с командами оболочки
Но что если в сценарии потребуется перехватить данные, выводимые командой? Функция os.system просто запускает команду оболочки, тогда как функция os.popen дополнительно соединяется со стандартными потоками ввода-вывода команды, - обратно возвращается объект, подобный файлу, по умолчанию соединенный с выводом команды (если передать функции popen флаг режима w, то вместо этого произойдет подключение к потоку ввода команды). Используя этот объект для чтения данных, выводимых командой, запущенной с помощью popen, можно перехватывать текст, в обычных условиях появляющийся в окне консоли, где вводится команда:
>>> open('helloshell.py').read()
"# a Python program\nprint(‘The Meaning of Life’)\n”
>>> text = os.popen('type helloshell.py').read()
>>> text
"# a Python program\nprint(‘The Meaning of Life’)\n”
>>> listing = os.popen('dir /B').readlines()
>>> listing
[‘helloshell.py\n’, ‘more.py\n’, ‘more.pyc\n’, ‘spam.txt\n’, ‘__init__.py\n’]
Здесь мы получаем содержимое файла сначала обычным способом (средствами Python для работы с файлами), а затем - как вывод команды оболочки type. Чтение вывода команды dir позволяет получить список файлов в каталоге, который затем можно обработать в цикле. В главе 4 будут представлены другие способы получения такого списка, и там же мы познакомимся с итераторами файлов, которые в большинстве программ делают ненужным вызов функции readlines, показанный в примере выше, за исключением отображения списка в интерактивной оболочке, как в данном случае (дополнительная информация по этой теме приводится также во врезке «subprocess, os.popen и итераторы» ниже).
До сих пор мы выполняли простые команды DOS, но поскольку эти функции могут запускать любые допустимые команды, с их помощью можно также запускать другие сценарии Python. Предположим, что каталог с выполняемым файлом интерпретатора Python в вашей системе находится в пути поиска файлов (чтобы можно было использовать короткую команду «python» вместо «C:\Python31\python»):
>>> os.system('python helloshell.py') # запустит программу на языке Python The Meaning of Life 0
>>> output = os.popen('python helloshell.py').read()
>>> output
‘The Meaning of Life\n’
Во всех этих примерах командные строки, передаваемые функциям system и popen, жестко «зашиты» непосредственно в программный код, но нет никаких причин, по которым программы на языке Python не могли бы создавать такие строки на этапе выполнения с помощью обычных строковых операций (+, % и других). Учитывая, что команды могут конструироваться и выполняться динамически, функции system и popen превращают сценарии на языке Python в гибкие и переносимые средства запуска и управления другими программами. Например, тестовый «управляющий» сценарий на языке Python можно использовать для запуска программ, написанных на любых языках программирования (например, C++, Java, Python), и анализа их вывода. Такой сценарий будет рассмотрен в главе 6. В следующей главе мы снова вернемся к функции os.popen, где будем рассматривать ее в соединении с проблемой перенаправления потоков ввода-вывода - как будет показано там, механизм перенаправления также может использоваться для передачи ввода в программы.
Альтернатива на основе модуля subprocess
Как уже говорилось, в последних версиях Python появился модуль subprocess, позволяющий добиться того же эффекта, что и функции os.system и os.popen. Вообще говоря, для этого придется написать дополнительный программный код, но этот модуль обеспечивает более полный контроль над подключением и использованием потоков ввода-вывода. Это особенно полезно для реализации сложных схем связывания потоков ввода-вывода.
Например, чтобы запустить простую команду оболочки, как мы делали это с помощью функции os.system выше, можно воспользоваться функцией call из нового модуля, которая действует похожим образом (чтобы запустить такую команду, как type, встроенную в оболочку Windows, требуется соблюсти дополнительные условия, хотя для запуска обычных выполняемых файлов, таких как python, этого не требуется):
>>> import subprocess
>>> subprocess.call('python helloshell.py') # напоминает os.system()
The Meaning of Life 0
>>> subprocess.call('cmd /C "type helloshell.py”') # встроенная команда
# a Python program print(‘The Meaning of Life’)
0
>>> subprocess.call('type helloshell.py', shell=True) # альтернативный способ
# a Python program # для встроенных команд
print(‘The Meaning of Life’)
0
Обратите внимание на аргумент shell=True в последнем вызове. Это платформозависимая особенность:
• Чтобы запустить встроенную команду оболочки в Windows, инструментам модуля subprocess, таким как call и Popen (об этой функции будет рассказываться ниже), необходимо передавать аргумент shell=True. Команды Windows, такие как type, требуют соблюдения дополнительных условий, но для запуска обычных выполняемых файлов, таких как python, этого не требуется.
• В Unix-подобных платформах, когда аргумент shell принимает значение False (по умолчанию), команда запускается непосредственно вызовом функции os.execvp, с которой мы встретимся в главе 5. Если в этом аргументе передать True, команда будет выполнена с помощью оболочки, при этом вы можете указать используемую оболочку в дополнительном аргументе.
Подробнее о некоторых из этих особенностей мы поговорим ниже, а пока достаточно будет запомнить, что в Unix-подобных системах вам может потребоваться передавать аргумент shell=True в некоторых примерах в этом разделе и в книге, если они предполагают использование таких особенностей оболочки, как путь поиска программ. Поскольку я запускаю примеры в Windows, этот аргумент я часто буду опускать.
Помимо имитации функции os.system, мы точно так же можем использовать этот модуль для имитации функции os.popen, чтобы запускать команды оболочки и получать ее вывод в нашем сценарии:
>>> pipe = subprocess.Popen('python helloshell.py', stdout=subprocess.PIPE)
>>> pipe.communicate()
(b’The Meaning of Life\r\n’, None)
>>> pipe.returncode
0
Здесь мы связали поток стандартного вывода команды оболочки с каналом и вызвали метод communicate, ожидающий завершения команды и принимающий текст, который она выводит в стандартный поток вывода и в стандартный поток ошибок. Код завершения команды доступен в виде атрибута, после того как она будет выполнена. Точно так же мы могли бы использовать отдельную функцию чтения потока стандартного вывода команды и отдельную функцию ожидания ее завершения (которая возвращает код завершения):
>>> pipe = subprocess.Popen('python helloshell.py', stdout=subprocess.PIPE)
>>> pipe.stdout.read()
b’The Meaning of Life\r\n’
>>> pipe.wait()
0
Фактически существует возможность прямой замены вызова os.popen объектом subprocess.Popen:
>>> from subprocess import Popen, PIPE
>>> Popen('python helloshell.py', stdout=PIPE).communicate()[0]
b’The Meaning of Life\r\n’
>>>
>>> import os
>>> os.popen('python helloshell.py').read()
‘The Meaning of Life\n’
Как видите, реализация относительно простых случаев с помощью модуля subprocess требует дополнительной работы. Но ситуация меняется в лучшую сторону, когда возникает необходимость гибкого управления потоками ввода-вывода. Фактически, благодаря возможности обрабатывать стандартные потоки вывода и ошибок команды похожими способами, модуль subprocess в Python 3.X заменил оригинальные функции os.popen2, os.popen3 и os.popen4, имевшиеся в Python 2.X. Теперь эти функции являются лишь частными случаями использования интерфейса объектов модуля subprocess. Поскольку в более сложных случаях использования этого модуля предполагается взаимодействие со стандартными потоками ввода-вывода, мы отложим дальнейшее обсуждение этого модуля, пока не познакомимся с механизмом перенаправления потоков в следующей главе.
Ограничения, присущие командам оболочки
Прежде чем двинуться дальше, вы должны запомнить два ограничения, связанные с функциями system и popen. Во-первых, хотя сами по себе эти функции хорошо переносимы, в действительности их применение переносимо лишь в той мере, в какой это относится к выполняемым ими командам. Предыдущие примеры с командами DOS dir и type, например, работают только в Windows, а для Unix-подобных платформ их следует изменить так, чтобы они выполняли команды ls и cat.
Во-вторых, важно помнить, что запуск файлов Python как самостоятельных программ таким способом очень отличается от импорта программных файлов и вызова функций, объявленных в них, и обычно происходит гораздо медленнее. Когда вызываются функции os.system и os.popen, им приходится запускать совершенно новую и независимую программу, выполняемую операционной системой (как правило, они запускают команды в виде новых процессов). При импорте программы в качестве модуля интерпретатор Python просто загружает и выполня-
ет код файла в том же процессе, генерируя объект модуля. При этом никакие другие программы не запускаются.8
Могут быть веские основания для построения системы в виде отдельных программ, и в следующей главе будут рассмотрены такие темы, как аргументы командной строки и потоки ввода-вывода, которые позволяют программам передавать информацию в обоих направлениях. Но в большинстве случаев операция импортирования является более быстрым и прямым способом построения систем.
Если вы серьезно намерены использовать эти вызовы, то следует знать, что вызов os.system обычно блокирует (то есть приостанавливает) вызывающую программу до завершения запущенной ею команды. В Linux и Unix-подобных платформах имеется возможность заставить команду выполняться независимо и параллельно с вызвавшей ее программой, добавив в конец командной строки оператор & выполнения в фоновом режиме:
os.system("python program.py arg arg &”)
В Windows запуск с помощью команды DOS start обычно также приводит к запуску команды в виде независимого процесса, выполняющегося параллельно:
os.system("start program.py arg arg”)
В действительности, такой способ запуска команд оказался настолько удобным, что в последние версии Python была добавлена функция os.startfile. Эта функция открывает файл с помощью программы, указанной в реестре Windows для файлов этого типа, как если бы был выполнен щелчок мышью на ярлыке этого файла:
os.startfile("webpage.html”) # open file in your web browser os.startfile("document.doc”) # open file in Microsoft Word os.startfile("myscript.py”) # run file with Python
Функция os.popen обычно не блокирует вызывающую программу (она, по определению, должна иметь возможность читать или писать в возвращаемый объект файла). Тем не менее вызывающая программа иногда все же может оказаться заблокированной в любой операционной системе, Windows или Linux, если объект канала будет закрыт до завершения порожденной программы (например, при сборке мусора) или когда канал будет прочитан до исчерпания (например, с помощью метода read() канала). Как будет показано далее в этой части книги, для параллельного исполнения программ без блокирования можно использовать функции os.fork/exec в Unix и os.spawnv - в Windows.
Поскольку функции system и popen из модуля os, а также модуль subprocess попадают еще и в категорию средств запуска программ, перенаправления потоков ввода-вывода и средств взаимодействия процессов, они снова появятся в последующих главах, поэтому пока мы отложим их дальнейшее обсуждение. Если вам уже сейчас необходимы дополнительные подробности, прочитайте раздел, касающийся вопросов перенаправления потоков ввода-вывода в следующей главе, и раздел, описывающий получение списка каталогов, в главе 4.
Другие элементы, экспортируемые модулем os
Мы рассмотрели в модуле os, что смогли. Поскольку все другие инструменты модуля os еще труднее оценить вне контекста более крупных приложений, мы отложим их более пристальное рассмотрение до последующих разделов. Но, чтобы дать вам представление о характере этого модуля, ниже приводится краткий справочный обзор. Среди прочего на вооружении модуля os состоят:
os.environ
Извлекает и устанавливает значения переменных окружения оболочки.
os.fork
Запускает новый дочерний процесс в Unix-подобных системах.
os.pipe
Обеспечивает обмен данными между программами.
os.execlp
Запускает новые программы.
os.spawnv
Запускает новые программы с возможностью низкоуровневого управления.
os.open
Открывает файл с использованием файлового дескриптора.
os.mkdir
Создает новый каталог.
os.mkfifo
Создает новый именованный канал.
os.stat
Получает низкоуровневую информацию о файле.
os.remove
Удаляет файл по строке пути к нему.
os.walk
Применяет функцию или тело цикла ко всем элементам в дереве каталогов.
И так далее. Заранее предупреждаю: модуль os предоставляет группу функций для открытия, чтения и записи файлов, но все они используют доступ к файлам на низком уровне и кардинально отличаются от встроенных файловых объектов Python stdio, создаваемых с помощью встроенной функции open. Обычно во всех случаях следует использовать встроенную функцию open, а не модуль os, кроме очень специфических потребностей обработки файлов (например, когда требуется открыть файл, заблокировав доступ к нему из других программ).
В следующей главе мы будем использовать инструменты из модулей sys и os для решения обычных системных задач, но объем данной книги не позволяет приводить полные списки содержимого встречающихся модулей. Если вы этого еще не сделали, ознакомьтесь с содержимым таких модулей, как os и sys, обратившись к ресурсам, описанным выше. А теперь перейдем к исследованию дополнительных системных инструментов в контексте более широких понятий системного программирования.
subprocess, os.popen и итераторы
В главе 4 мы будем исследовать итераторы файлов, но перед тем, как взять эту книгу, вы наверняка уже ознакомились с основами. Поскольку объекты, возвращаемые функцией os.popen, обладают итераторами, позволяющими читать данные по одной строке за раз, использование метода readlines этих объектов обычно является излишним. Например, ниже приводится пример чтения строк, которые выводятся другой программой, без явного использования методов чтения:
>>> import os
>>> for line in os.popen('dir /B *.py'): print(line, end='')
helloshell.py more.py __init__.py
Интересно, что в Python 3.1 функция os.popen реализована с использованием объекта subprocess.Popen, с которым мы познакомились в этой главе. Вы можете убедиться в этом, заглянув в файл os.py в стандартной библиотеке Python (в Windows вы найдете этот файл в каталоге C:\Python31\Lib). Результатом вызова функции os.popen является объект, управляющий объектом Popen и его каналами:
>>> I = os.popen('dir /B *.py')
>>> I
Этот объект-обертка канала определяет метод__iter__, поэтому он
поддерживает возможность итераций по строкам, которые могут выполняться автоматически (например, в цикле for, как показано выше) или вручную. Любопытно отметить, что, несмотря на наличие в объекте-обертке поддержки прямого вызова метода__next__,
как если бы этот объект обладал собственным итератором (подобно простым файлам), тем не менее он не поддерживает встроенную функцию next, хотя последняя, вероятно, просто вызывает метод __next__:
>>> I = os.popen('dir /B *.py')
>>> I.__next__()
'helloshell.py\n'
>>> I = os.popen('dir /B *.py')
>>> next(I)
TypeError: _wrap_close object is not an iterator
Причина такого поведения скрыта глубоко в недрах реализации -
прямой вызов метода__next__перехватывается методом__ge-
tattr__, определенном в объекте-обертке канала, и преобразуется
в вызов метода обернутого объекта. Но функция next обращается к механизму перегрузки операторов, который в Python 3.X действует в обход метода__getattr__, когда производится вызов методов со специальными именами, такими как__next__. Поскольку
объект-обертка канала не определяет собственный метод__next__,
обращение к нему не перехватывается и не передается обернутому объекту, что приводит к ошибке при вызове встроенной функции next. Как детально объясняется в книге «Изучаем Python», метод
__getattr__обертки не вызывается по той простой причине, что
в Python 3.X поиск методов начинается не с экземпляра, а с класса.
Такое поведение может быть или не быть ожидаемым, но вам не придется беспокоиться об этом при выполнении итераций по строкам в канале с помощью цикла for, генераторов и других инструментов. Тем не менее, чтобы обеспечить выполнение итераций вручную, необходимо сначала вызвать встроенную функцию iter -
она вызовет метод__iter__объекта-обертки канала и обеспечит
корректную поддержку обоих способов перемещения по строкам:
>>> I = os.popen('dir /B *.py')
>>> I = iter(I) # так поступает цикл for
>>> I. next () # теперь обе формы итераций действуют правильно
'helloshell.py\n'
>>> next(I)
‘more.py\n’
3
Контекст выполнения сценариев
«Ваши аргументы, пожалуйста!»
Сценарии на языке Python выполняются не в вакууме (хотя, возможно, вы слышали что-то иное). В зависимости от платформы и процедуры запуска программы на языке Python выполняются в определенном контексте, то есть обладают информацией, которая автоматически передается операционной системой при запуске программы. Например, у сценариев есть доступ к следующим видам входных данных системного уровня и интерфейсов:
Текущий рабочий каталог
Функция os.getcwd предоставляет доступ к каталогу, из которого запущен сценарий и значение которого неявно используют многие инструменты для работы с файлами.
Аргументы командной строки
sys.argv предоставляет доступ к словам в команде, с помощью которой была запущена программа и которые играют роль входных данных сценария.
Переменные оболочки
os.environ предоставляет интерфейс к переменным окружения, созданным в охватывающей оболочке (или родительской программой) и передаваемым сценарию.
Стандартные потоки ввода-вывода
sys.stdin, stdout и stderr экспортируют три потока ввода-вывода, лежащие в центре инструментов командной оболочки, и могут использоваться в сценариях с помощью функции print, os.popen и модулем
subprocess, представленными в главе 2, с помощью класса io. StringIO и других инструментов.
Такие инструменты могут играть роль входных данных сценариев, параметров настройки и так далее. В этой главе мы исследуем все эти четыре инструмента работы с контекстом - их интерфейсы в Python и типичные области их использования.
Текущий рабочий каталог
Понятие текущего рабочего каталога (current working directory, CWD) оказывается ключевым при выполнении некоторых сценариев: это всегда неявно определенное место в файловой системе, где предполагается размещение обрабатываемых сценарием файлов, если в их именах отсутствует абсолютный путь к каталогу. Как мы уже видели, функция os.getcwd позволяет сценарию получить имя текущего рабочего каталога в явном виде, а функция os.chdir позволяет сценарию переместиться в новый текущий рабочий каталог.
Однако имейте в виду, что для имен файлов, не содержащих полного пути, подразумевается, что они находятся в текущем рабочем каталоге, и это не имеет никакого отношения к переменной окружения PYTHONPATH. С технической точки зрения сценарий всегда запускается из текущего рабочего каталога, а не из каталога, содержащего файл сценария. Напротив, при импортировании модулей поиск всегда начинается в каталоге, содержащем сценарий, а не в текущем рабочем каталоге (если только сценарий не размещен в текущем рабочем каталоге). Поскольку это тонкое различие, на котором часто попадаются новички, изучим его более подробно.
Текущий рабочий каталог, файлы и путь поиска модулей
Если запустить сценарий Python, введя в команду, например, python dir1\dir2\file. py, - текущим рабочим каталогом будет каталог, в котором вы находились при вводе этой команды, но не dirl\dir2. С другой стороны, Python автоматически добавляет путь к каталогу, где находится сценарий, в начало пути поиска модулей, поэтому file.py всегда сможет импортировать другие файлы из dirl\dir2, откуда бы он ни был запущен. Чтобы проиллюстрировать это, напишем простой сценарий, выводящий имя текущего рабочего каталога и путь поиска модулей:
C:\...\PP4E\System> type whereami.py
import os, sys
print(‘my os.getcwd =>’, os.getcwd()) # вывод текущего рабочего каталога print(‘my sys.path =>’, sys.path[:6]) # вывод первых 6 каталогов в пути поиска input() # ожидает нажатия клавиши
Теперь при запуске этого сценария в том каталоге, где он находится, будет выбран ожидаемый текущий рабочий каталог, и имя этого каталога будет добавлено в начало пути поиска. Мы уже встречались со списком sys.path, содержащим путь поиска модулей, - первый его элемент может быть пустой строкой, обозначающей текущий рабочий каталог при работе в интерактивной оболочке; здесь большая часть пути к текущему рабочему каталогу усекается до «...» при отображении:
C:\...\PP4E\System> set PYTHONPATH=C:\PP4thEd\Examples
C:\...\PP4E\System> python whereami.py
my os.getcwd => C:\...\PP4E\System
my sys.path => [‘C:\\...\\PP4E\\System’, ‘C:\\PP4thEd\\Examples’, ...другие
элементы... ]
Если запускать этот сценарий из других каталогов, вслед за нашим перемещением переместится и текущий рабочий каталог (это каталог, в котором вводятся команды), а Python будет добавлять в начало пути поиска модулей каталог, где находится сам сценарий, что позволит сценарию по-прежнему видеть файлы в своем исходном каталоге. Например, если запустить сценарий, поднявшись на один уровень (..), каталог System будет добавлен в начало списка sys.path и станет первым каталогом, в котором Python станет искать модули, импортируемые сценарием whereami.py: первый элемент списка будет нацеливать импорт обратно на каталог, содержащий выполняемый сценарий. Однако поиск файлов, имена которых не содержат полного пути, будет выполняться относительно текущего рабочего каталога (C:\PP4thEd\Examples\ PP4E), а не в его подкаталоге System:
C:\...\PP4E\System> cd ..
C:\...\PP4E> python System\whereami.py
my os.getcwd => C:\...\PP4E
my sys.path => [‘C:\\...\\PP4E\\System’, ‘C:\\PP4thEd\\Examples’, ...другие
элементы... ]
C:\...\PP4E> cd System\temp
C:\...\PP4E\System\temp> python ..\whereami.py
my os.getcwd => C:\...\PP4E\System\temp
my sys.path => [‘C:\\...\\PP4E\\System’, ‘C:\\PP4thEd\\Examples’, ...]
В результате поиск файлов, имена которых в сценарии не содержат полных путей, будет выполняться в том месте, где была введена команда (os.getcwd), но операции импортирования по-прежнему будут иметь доступ к каталогу, где находится выполняемый сценарий (через первый элемент в списке sys.path). Наконец, если файл запускается щелчком на ярлыке, текущим рабочим каталогом станет каталог, содержащий файл, на котором выполнен щелчок. Например, следующие строки будут выведены в новом окне консоли DOS при двойном щелчке на whereami.py в проводнике Windows:
my os.getcwd => C:\...\PP4E\System
my sys.path => [‘C:\\...\\PP4E\\System’, ...more... ]
В данном случае текущий рабочий каталог, используемый для поиска файлов, и первый каталог в пути поиска модулей совпадают с каталогом, содержащим файл сценария. Обычно все действует так, как предполагается, но здесь вас могут подстерегать две опасности:
• Имена файлов должны содержать полные пути к каталогам, если заранее не известно, из какого каталога будет запущен сценарий.
• Сценарии командной строки не всегда могут использовать текущий рабочий каталог, чтобы увидеть импортируемые файлы, не находящиеся в собственных каталогах сценариев, - для доступа к модулям из других каталогов следует использовать переменную окружения PYTHONPATH и пути к импортируемым пакетам.
Например, сценарии из этой книги всегда могут импортировать другие файлы из собственного исходного каталога, не указывая путь к импортируемому пакету (import file here), независимо от того, как они запущены. Но, чтобы отыскать файлы в другом месте в дереве каталогов примеров, путь поиска должен проходить через корень пакета РР4Е (from PP4E.dir1.dir2 import filethere), даже если запускать сценарии из каталога, содержащего нужный внешний модуль. Как обычно, чтобы обеспечить возможность импортирования модулей, имя каталога PP4E\dirl\dir2 можно также добавить в PYTHONPATH, чтобы сделать file-there видимым отовсюду, без указания пути к импортируемому пакету (хотя лишние каталоги в PYTHONPATH увеличивают вероятность конфликта имен). В любом случае импорт всегда осуществляется из исходного каталога сценария или из другого каталога, находящегося в пути поиска Python, а не из текущего рабочего каталога.
Текущий рабочий каталог и командные строки
Это различие между текущим рабочим каталогом и путями поиска модулей объясняет, почему многие сценарии в данной книге, которые должны действовать в текущем рабочем каталоге (а не в том, имя которого передано), запускаются командной строкой вида:
C:\temp> python C:\...\PP4E\Tools\cleanpyc.py обработка cwd
В данном примере сам файл сценария на языке Python находится в каталоге C:\..\PP4E\Tools, но поскольку он запускается из C:\temp, то обрабатывает файлы, содержащиеся в C:\temp (то есть в текущем рабочем каталоге, а не исходном каталоге сценария). Чтобы обработать с помощью такого сценария файлы, находящиеся где-то в другом месте, нужно просто изменить текущий рабочий каталог с помощью команды cd и перейти в каталог, который должен быть обработан:
C:\temp> cd C:\PP4thEd\Examples
C:\PP4thEd\Examples> python C:\...\PP4E\Tools\cleanpyc.py обработка cwd
Поскольку текущий рабочий каталог всегда неявно определен, сценарий узнает, какой каталог должен быть обработан, от команды cd с той же степенью определенности, что и при явной передаче имени каталога сценарию, как показано ниже (примечание, касающееся переносимости: в других примерах команд вам может потребоваться заключить *.py в кавычки, чтобы предотвратить подстановку имен файлов, которая выполняется в некоторых оболочках Unix):
C:\...\PP4E\Tools> python find.py *.py C:\temp обработка указанного
каталога
В этой командной строке текущим рабочим каталогом является каталог, содержащий запускаемый сценарий, который должен быть выполнен (обратите внимание на отсутствие пути в имени файла сценария). Но поскольку этот сценарий обрабатывает каталог, явно указанный в командной строке (C:\temp ), в данном случае не имеет никакого значения, какой каталог является текущим рабочим каталогом. Наконец, если потребуется выполнить такой сценарий, расположенный в некотором другом каталоге, для обработки файлов, находящихся в третьем каталоге, можно просто указать пути каталогов для обоих:
C:\temp> python C:\...\PP4E\Tools\find.py *.cxx C:\PP4thEd\Examples\PP4E
В этом случае для импорта будут доступны файлы в исходном каталоге сценария PP4E\Tools , а обрабатываться будут файлы в каталоге, указанном в командной строке, при этом текущим рабочим каталогом будет совершенно другой каталог (C:\temp ). Использование последней формы требует больше вводить с клавиатуры, тем не менее в этой книге вам еще не раз встретятся различные текущие рабочие каталоги и командные строки с явными путями к сценариям.
Аргументы командной строки
Модуль sys позволяет также получить те слова, которые были введены в команде, запустившей сценарий на языке Python. Эти слова обычно называются аргументами командной строки и находятся во встроенном списке строк sys.argv. Программисты на C могут заметить сходство с массивом argv в языке C (массивом строк). В интерактивном режиме смотреть особенно не на что, так как для запуска Python в этом режиме ему не требуется передавать аргументы командной строки:
>>> import sys
>>> sys.argv
[‘’]
Чтобы действительно увидеть аргументы, нужно запустить сценарий из командной строки. В примере 3.1 приводится простой до безобразия сценарий, который всего лишь выводит список argv для изучения.
Пример 3.1. PP4E\System\testargv.py
import sys
print(sys.argv)
Если запустить этот сценарий, он выведет список аргументов командной строки. Обратите внимание, что первым элементом всегда является имя самого выполняемого сценария, независимо от способа запуска (смотрите врезку «Выполняемые сценарии в Unix» далее в этой главе):
C:\...\PP4E\System> python testargv.py
[‘testargv.py’]
C:\...\PP4E\System> python testargv.py spam eggs cheese
[‘testargv.py’, ‘spam’, ‘eggs’, ‘cheese’]
C:\...\PP4E\System> python testargv.py -i data.txt -o results.txt
[‘testargv.py’, ‘-i’, ‘data.txt’, ‘-o’, ‘results.txt’]
Последняя команда в этом фрагменте иллюстрирует общепринятое соглашение. Подобно аргументам функции, параметры командной строки иногда передаются по позиции, а иногда по имени с помощью пары «-имя значение». Например, пара -i data.txt означает, что значением ключа -i является data.txt (например, имя входного файла). В качестве параметров командной строки допускается передавать любые слова, но обычно программы накладывают на них некоторые структурные ограничения.
Аргументы командной строки играют в программах такую же роль, как аргументы в функциях: они просто позволяют передать в программу информацию, которая может быть различной для каждого запуска программы. То обстоятельство, что они не определяются жестко в программном коде, позволяет использовать сценарии более универсальными способами. Например, аргументы командной строки могут использоваться для передачи имен файлов сценариям, их обрабатывающим, - взгляните на сценарий more.py в главе 2 (пример 2.1), который был нашим первым примером. Другие сценарии могут принимать флаги режима обработки, адреса Интернета и так далее.
Анализ аргументов командной строки
Однако при регулярном использовании аргументов командной строки вы можете обнаружить, что писать код, который вылавливает в списке слова, неудобно. Обычно при запуске программы преобразуют список аргументов в структуры, более удобные для обработки. Ниже приводится один из способов реализации такого преобразования: сценарий в примере 3.2 просматривает список argv в поисках пар -optionname op-tionvalue и помещает их в словарь, используя в качестве ключей имена параметров, чтобы потом их было легче извлекать.
Пример 3.2. PP4E\System\testargv2.py
"собирает параметры командной строки в словаре”
def getopts(argv): opts = {} while argv:
if argv[0][0] == ‘-’: # поиск пар "-name value”
opts[argv[0]] = argv[1] # ключами словарей будут имена параметров argv = argv[2:] else:
argv = argv[1:] return opts
if__name__== ‘__main__’: