Погружение в Python 3 (Dive into Python 3) ( PDFDrive.com )

До загрузки: 30 сек.



Благодарим, что скачиваете у нас :)

Если, что - то:

  • Поделится ссылкой:
  • Документ найден в свободном доступе.
  • Загрузка документа - бесплатна.
  • Если нарушены ваши права, свяжитесь с нами.
Формат: pdf
Найдено: 26.06.2020
Добавлено: 30.09.2020
Размер: 4.15 Мб

Погружение 
PWKRQ3
1. Что нового в «Погружении в Python 3»
2. Установка Python
3. Ваша первая программа на Python
4. Встроенные типы данных
5. Генераторы
6. Строки
7. Регулярные выражения
8. Замыкания и генераторы
9. Классы и итераторы
10. Подробнее об итераторах
11. Тестирование
12. Рефакторинг
13. Файлы
14. XML
15. Сериализация объектов Python
16. HTTP и веб -сервисы
17. Пример: перенос chardet на PWKRn 3
18. Создание пакетов библиотек
19. Перенос кода на PWKRQkihfhsvxWR3
20. Особые названия методов
21. Куда пой ти
22. Устранение проблем
23. О книге
24. О переводе
25. Выходные данные

Автор: Марк Пилигрим (Mark Piligrim)
Переh^: ИнициатиgZy]jmiiw
Источник ориги нала: ru.wikisource.org
Лицензия: данный материал распространяется по лицензии GNU FDL .

2

Что нового 
« Погружении 
PWKRQ»

Isn’t this where we came in? =
Pink Floyd , The Wall
или «минус первый уровень»
Вы прочитали « Погружение в Python » и, может быть, даже купили бумажную
_jkbx KiZkb[h <um`_g_iehohagZ_l_3\WKRn 2. Вы готоuhdmgmlvkyk
голоhc\3\WKRn 3… Если всё это про ZkqblZcl_^Zevr_ ?kebqlh -либо из
этого не_jghам следует начать с начала.)
Вместе с Python 3 постаey_lky скрипт под назZgb_f . Изучите его. 2to3
Ihex[bl__]hBkihevamcl__]h I_j_ghkdh^ZgZ3\WKRQ 3 с помощью
» — спраhqgbdih\k_fl_fbaf_g_gbyfdhlhju_ может проделать 2to3 2to3
ZlhfZlbq_kdbIhkdhevdmfgh]h_bawlh]h — изм енения kbglZdkbk_emqr_
k_]hgZqZlvbf_gghkgbo( теперь функция, не работает и т. д.) print `x`
«Пример: перенос на Python 3» рассказыZ_lhfh_c  конце концов, chardet
mki_rghc ihiuld_i_j_g_klbh^gmg_ljbbZevgmx[b[ebhl_dmk3\WKRQ 2 на
Python 3. Это может Zfihfhqv:fh`_lbg_ihfhqv КриZyh[mq_gby
получается довольно крутой из -за того, что ZfkgZqZeZg_h[oh^bf о хоть
немного разобраться в этой библиотеке, чтобы понимать, что именно в ней
поломалось и как я это испраbeQZklhijh]jZffuehfZxlkygZ строках
(strings). Кстати, о строках…

3

Строки… Ох… С чего бы начать?… В Python 2 были строки ( ) и юникодные str
строки ( ). В Python 3 — байты ( ) и строки ( ). То есть все unicode bytes string
kljhdbklZebl_i_jvxgbdh^gufbZdh]^Zohlbl_jZ[hlZlvkdmq_c[Zclhu
bkihevam_l_ghuclbi . Python 3 никогда автоматически не bytes
ij_h[jZahuZ_lkljhdb[ZclubebgZh[hjhlihw lhfm_kebug_m_j_gu
qlhbagboubf__l_\dZdhc -то определённый момент, Zrdh^ihqlb
на_jgydZihehfZ_lky<]eZе о строках эта ситуация разбирается
подробнее.
Байты и строки ещё не раз будут пояeylvky книге.
 В гла_NZceuы узнаете разницу между чтением файлов 
«двоичном» и «текстоhfj_`bfZoQl_gb_ baZibkv nZceh\\
текстоhfj_`bf_lj_[m_lmdZaZgb_iZjZf_ljZ (кодироdw ). encoding
Одни файловые методы при работе с текстоufbnZceZfb
подсчитыZxlkbfолы, другие считают байты. Если в Zr_cijh]jZff_
предполагается, что один симhe == одному байту, она будет падать на
многобайтоuokbfолах.
 В гла_+773b\_x -серbkufh^mev принимает заголоdbb http lib2
^Zggu_ih HTTP . Заголовки HTTP возjZsZxlkydZdkljhdbZl_eh
от_lZ+773озвращается в b^_[Zclh.
 В гла_K_jbZebaZpbyh[t_dlh\3\WKRQы узнаете, почему модуль
3\WKRn 3 определяет ноucnhjfZl^Zgguog_khместим ый с pickle
Python 2. (Подсказка: это из -за различий между байтами и строками.)
Ещё есть JSON , который воk_g_ih^^_j`bает тип . Я покажу bytes
ZfdZdkijZblvkykwlbfh]jZgbq_gb_f
 Глава « Пример: перенос на Python 3» — просто кроваh_ chardet
f_kbhba[Zclh bkljhd
Даже если ug_aZ^mfuаетесь о Юникоде (рано или поздно всё раgh
придётся), uaZohlbl_ijhqblZlvhnhjfZlbjhании строк в Python 3, оно
со_jr_gghhlebqZ_lkyhllZdh\h]h Python 2.
Итераторы 3\WKRn 3 _a^_bk_cqZkyjZa[bjZxkv\gbogZfg ого лучше, чем
пять лет назад, когда я писал «Погружение 3\WKRQ<Zflh`_g_h[oh^bfh
разбираться gboihlhfmqlhfgh`_klо функций, которые 3\WKRn 2
haращали списки, теперь 3\WKRn 3 haращают итераторы. Как минимум,
следует прочитать lhjmxihe оbgm]eZы «Итераторы» и вторую половину
глаuIh^jh[g__h[bl_jZlhjZo.
По многочисленным просьбам я добаbeijbeh`_gb_Hkh[u_gZaания
методов», которое немного похоже на глаmFh^_ev^Zgguo^hdmf_glZpbbih
Python , только с приколами.
Когда я писал «Погружение 3\WKRQ\k_^hklmigu_gZlhlfhf_gl
библиотеки для работы с XML были жутким отстоем. ВпоследстbbNj_^jbd

4

Лунд (Fredrik Lundh) написал ElementTree , которая уже соk_fg_hlklhc
Питоновские боги, показав свою мудрость, g_^jbeb(OHPHQW7UHH стандартную
библиотеку , и теперь она состаey_lhkghу моей новой глаuijh;0/
Старые способы разбора XML всё ещё доступны, но их надо избегать, потому
что они отстойные!
Ещё из нового 3\WKRn — не в языке, а в сообщест| — пояe_gb_
репозиториев jh^_ The Python Package Index (PyPI) . К Python прилагаются
утилиты для пакетироZgbyашего кода klZg^ZjlguonhjfZlZob
дальнейшего его распространения через PyP I. Подробнее ]eZе
«ПакетироZgb_[b[ebhl_d3\WKRn ».

5

УстаноdZ
PWKRn

Tempora mutantur nos et mutamur in illis. ~ Меняются j_f_gZbfuf_gy_fky
f_kl_kgbfb. =
дреg_jbfkdZyih]hорка
Погружение
Добро пожаловать 3\WKRn 3. В этой гла_ы установите Python 3, ту его
_jkbxdhlhjZyih^oh^blbf_ggh\Zf.
Какой Python вам подходит?
Первое, что Zfg_h[oh^bfhijh^_eZlvk3\WKRQ, — устаноblv_]hBebwlh
уже сделано?
Если ukh[bjZ_l_kvjZ[hlZlvk3\WKRQgZm^Zezgghfk_jере, ваш хостинг -
проZc дер, hafh`ghm`_mklZghил Python 3. Если у Zk^hfZrgbc
компьютер с Linux , Python 3 тоже может быть уже устаноe_g<[hevrbgklе
популярных дистрибутиZo*18/LQX[ihmfheqZgbxmklZghлен Python 2,
немногие (но их число растёт) также dexqZxl3\WKRn 3. Mac OS X dexqZ_l
консольную _jkbx3\WKRn 2, но до сих пор не dexqZ_l3\WKRn 3. В Microsoft
Windows не oh^blgbdZdZyерсия Python. Но не отчаиZcl_kv3\WKRQfh`gh
устаноblv несколько кликов, незаbkbfhhlашей операционной системы.
Простейший спо соб про_jblvmklZghлен ли Python 3 ашем Linux или
Mac OS X, — это открыть командную строку. В Linux поищите программу
«Терминал» («Terminal» ) в меню приложений («Applications»). Она может
находиться в подменю «Стандартные» («Accessories» ) или «Систем ные
утилиты» («System» ). В Mac OS X в папке должно /Application/Utilities/
[ulvijbeh`_gb_ «Terminal.app» .

6

Получиijb]eZr_gb_dhfZg^ghckljhdbijhklh_^bl_ (строчными python3
[md\Zfb[_aijh[_eh\ bihkfhljbl_qlhijhbahc^zlGZfh_c^hfZrg_c
Linux -системе Python 3 уже установлен, и эта команда запускает
интерактиgmx оболочку Python .
mark@atlantis:~$ python3
Python 3.0.1+ (r301:69556, Apr 15 2009, 17:25:52)
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more informati
on.
>>>
(Чтобы uclbbabgl_jZdlbной оболочки Python, едите и exit()
gZ`fbl_ ↵ .) Enter
Мой хостинг -провайдер тоже предостаey_l/LQX[k^hklmihfddhfZg^ghc
строке, но Python 3 на сер_j_g_mklZghлен. (Фу!)
mark@manganese:~$ python3
bash: python3: command not found
Итак, _jgzfkyd\hijhkmkdhlhjh]hgZqZekywlhljZa^_eDZdhc3\WKRQам
подходит?» Любой, который работает на Zr_fdhfivxl_j_.
Читайте далее инструкции по устаноd_gZ:LQGRZVbeb
перейдите к установке на Mac OS X, на Ubuntu Linux или на
другие платформы .

Установка на Microsoft Windows
Сегодня сущестmxl:LQGRZV^ey^ух архитектур : 32 -битной и 64 -битной.
Конечно, существуют и разные _jkbb Windows — XP , Vista , Windows 7 —
Python работает на k_o них. Важнее различие между 32 -битной и 64 -битной
архитектурами. Если ug_agZ_l_dZdZyZjobl_dlmjZашего компьютера, это,
_jhylgh2 бита.
Посетите . / и скачайте соот_lklующий устаноhqg ый пакет python org/download
Python 3 для Zr_cZjobl_dlmju:LQGRZV<Zrыбор будет примерно таким:
 Python 3.1 Windows installer (Windows binary — does not include source)
 Python 3.1 Windows AMD64 installer (Windows AMD64 binary — does not
include source)

7

Я не хочу публикоZlva^_kvijyfu_kkuedbihlhfmqlh\3\WKRQihklhyggh
происходят мелкие обноe_gbybyg_ohqmhdZaZlvkydjZcgbf_keb\uдруг
пропустите Z`gu_h[ghления. Устанаebайте самую с_`mxерсию
Python 3.x, если только у Zkg_ldZdbo -либ о объективных причин поступить
иначе.
1. Диалог Windows: предупреждение о безопасности при открытии файла
По окончании загрузки откройте .msi -файл. Windows покажет
предупреждение о безопасности, потому что uiulZ_l_kvaZimklblv
исполнимый код. Официальный ус таноhqguciZd_l3\WKRQbf__l
цифроmxih^ibkv Python Software Foundation , некоммерческой
организации, курирующей разработку Python. Опасайтесь подделок!
Нажмите кнопку «Запустить» («Run»), чтобы запустить програ мму
устаноdb3\WKRn 3.
2. Программа устаноdb3\WKRQыбор типа устаноdb3\WKRn 3.1 Перuc
hijhkdhlhjucaZ^Zzlijh]jZffZmklZghки: устаноblv3\WKRn 3 для
k_oihevahателей компьютера или только для ZkIhmfheqZgbx
u[jZghlет «устаноblv^ey\k_o пользоZl_e_cb_kebmас нет
причин u[bjZlv^jm]hcариант, его следует остаblv H^gZba
hafh`guoijbqbgmklZghки «только для себя» — это устаноdZgZ
рабочий компьютер, где ZrZmqzlgZyaZibkvg_bf__l
администратиguoiheghfhqbcGh таком случ ае почему u
устанаebаете Python без разрешения системного администратора? Не
imluайте меня g_ijbylghklb
Нажмите кнопку «Далее» («Next»), чтобы подт_j^blvыбор типа
устаноdb.
3. Программа устаноdb3\WKRQыбор директории назначения Затем
програм ма устаноdbij_^eh`blыбрать директорию назначения. По
умолчанию все _jkbb3\WKRn 3.1.x предлагают устаноdm директорию
, для большинстZihevahателей это должно работать, C: \Python31 \
_kebmZkg_lhkh[uoijbqbgbaf_gblvwlhliZjZf_ljg_f_gycl__]h
?kebmZkhl^_evguceh]bq_kdbc^bkd^eymklZghdbijbeh`_gbc\u
fh`_l_u[jZlv_]hihevamykv\kljh_ggufbbgkljmf_glZfbbebijhklh
ibkZlvimlvkhhl_lklmxs_fihe_h^Z3\WKRQfh`ghmklZghblv
g_lhevdhgZ^bkd ghgZex[hc^bkdex[mxiZidm C:
Нажмите кнопку «Далее» («Next»), чтобы подт_j^blvыбор директории
назначения.
4. Программа устаноdb3\WKRQыбор компоненто3\WKRn 3.1 Следующая
страница u]ey^blkeh`ghghgZkZfhf^_e_wlhg_lZdDZdо многих
других программах устаноdbmас есть hafh`ghklvhldZaZlvkyhl

8

устаноdbex[h]hbadhfihg_glh Python 3. Если сh[h^gh]h
пространстZgZ^bkd_kh\k_ffZehы можете искл ючить некоторые
компоненты.
o Опция РегистрироZlvjZkrbj_gby (Register Extensions )
позhey_lам запускать скрипты Python (файлы с расширением
) дhcgufdebdhfihbdhgd_J_dhf_g^m_lkyghg_h[yaZl_evgh .py
WlZhipbyg_aZgbfZ_lf_klZgZ^bkd_ihwlhfmg _lhkh[h]h
kfukeZ_zbkdexq_gbb
o Tcl/Tk — это графическая библиотека, используемая оболочкой
Python, которая будет использоZlvkygZijhly`_gbb\k_cdgb]bY
настоятельно рекомендую остаblvwlmhipbx.
o Опция Документация (Documentation ) устанаebает фа йл
спраdbkh^_j`ZsbcagZqbl_evgmxqZklvbgnhjfZpbbk
docs.python.org . Рекомендуется, если у Zk dial -up или ограниченный
доступ к Интернету.
o Полезные скрипты (Utility Scripts ) dexqZxlkdjbil , 2to3.py
подробнее о котором umagZ_l_gb`_G_h[oh^bfh_kebы хотите
узнать о переносе на Python 3 сущестmxs_]hdh^ZgZibkZggh]hgZ
Python 2. Если у Zkg_lkms_klующего кода на Python 2, можете
убрать эту опцию.
o ТестоucgZ[hj (Test Suite ) — это коллекция скрипто
исп ользуемых для тестироZgbykZfh]h3\WKRQ<wlhcdgb]_fubo
использоZlvg_[m^_f^Zbygbdh]^Zg_bkihevahал при
программироZgbbgZ3\WKRQKhершенно необязательно.
5. Программа устаноdb3\WKRQlj_[h\Zgbyd^bkdh\hfmijhkljZgklу Если
ug_agZ_l_lh чно, сколько у Zk^bkdh\h]hijhkljZgklа, нажмите
кнопку «ИспользоZgb_^bkdZ 'LVN8VDJH Ijh]jZffZmklZghки
покажет список логических дисков, посчитает, сколько пространстZ
доступно на каждом из них и сколько останется после устаноdb
Нажмите кн опку «OK», чтобы вернуться на страницу u[hjZ
компоненто.
6. Программа устаноdb3\WKRQhldexq_gb_hipbbL_klhый набор»
экономит 7908 Кбайт на жёстком диске Если вы решите отключить опцию,
нажмите кнопку перед ней и в uiZ\r_ff_gx\u[_jbl_Dhfihg_gl
полностью недоступен» («Entire feature will be unavailable»). Например,
исключение тестоh]hgZ[hjZkwdhghfblам 7908 Кбайт дискоh]h
пространстZ
Нажмите кноп ку «Далее» («Next»), чтобы подт_j^blvыбор опций.
7. Программа устаноdb3\WKRQbg^bdZlhjijh]j_kkw Программа устаноdb
скопирует k_g_h[oh^bfu_nZceu u[jZggmx^bj_dlhjbx
назначения. (Это происходит так быстро, что скриншот удалось сделать
только с третьей попытки!)

9

8. [|Программа устаноdb3\WKRQmklZghка завершена. Выражаю особую
bg^mkh\mx[eZ]h^w рность Марку Хэммонду (Mark Hammond), щедро
посylbшему много лет работе на Windows -напраe_gbb[_ag_]h
Python для Windows был бы kz_sz3\WKRQ^ey DOS .]] Нажмите кнопку
«Готоh )LQLVK qlh[uaZdjulvijh]jZffmmklZgh\db. [wap -robin.com
9. Оболочка Py thon для Windows, графическая интерактиgZyh[hehqdZ^ey
Python В меню «Пуск» должен появиться ноucimgdlih^gZaанием
Python 3.1. Внутри него будет программа IDLE. Кликните на ней, чтобы
запустить интерактиgmxh[hehqdm3\WKRQ.
Перейти к использованию оболочки Python .
Установка на Mac OS X
Все соj_f_ggu_dhfivxl_ju Macintosh используют процессоры Intel (как и
большинство компьютероk:LQGRZV KlZju_0Dc -и использоZeb
процессоры PowerPC . Вам не обязательно понимать разницу между ними,
потому что для всех Mac -о_klvh^bgmklZghочный пакет.
Посетите / и загрузите устаноhqguciZd_l^ey0DFLQWRVK python.org/download
Hg[m^_lgZauZlvkyijbf_jghlZd Python 3.1 Mac Installer Disk Image ,
номер _jkbbfh`_l[ulv^jm]bfAZ]jm`Zcl_bf_gghерсию 3.x, а не 2.x.
1. Содержимое устаноhqgh]hh[jZaZ3\WKRn Ваш браузер должен
аlhfZlbq_kdbkfhglbjhать образ диска и открыть окно Finder, чтобы
показать Zf_]hkh^| ржимое. (Если это не произошло, Zfg_h[oh^bfh
найти образ диска iZid_aZ]jmahdbkfhglbjhать его, кликнуgZgzf
дZ`^uHg[m^_lgZauаться примерно так: .) Образ python -3.1.dmg
^bkdZkh^_j`blg_kdhevdhl_dklh\uonZceh\ , , Build.txt License.txt
) и собст_gghmklZghочный пакет . ReadMe.txt Python.mpkg
ДZ`^udebdgbl_gZmklZghочном пакете , чтобы запустить Python.mpkg
ijh]jZffmmklZghdb3\WKRQ
2. Программа устаноdb3\WKRQwdjZgijbетстby Первая страница
программы устаноdb^ZzldjZldh_hibkZgb е и отсылает к файлу
(который ug_qblZebедь так?) за более подробными ReadMe.txt
k_^_gbyfb
Нажмите кнопку «Продолжить» («Continue») для продолжения устаноdb.
3. Программа устаноdb3\WKRQkедения о поддержиZ_fuoZjobl_dlmjZo
дисковом пространст| и допустимых папок назначения Следующая
страница содержит дейстbl_evgh\Z`gu_kедения: для Python
требуется Mac OS X 10.3 или более поздняя _jkby?kebы kz_sz

10

используете Mac OS X 10.2, Zf^_cklительно надо обноblvky$SSOH
перестала uimkdZlvh бноe_gby[_ahiZkghklb^ey\Zr_chi_jZpbhgghc
системы, и компьютер находится под hafh`ghcm]jhahc^Z`_dh]^Z
просто подключается к Интернету. Кроме того, на ней не работает
Python 3.
Нажмите кнопку «Продолжить» («Continue»), чтобы идти дальше.
4. Программа устаноdb3\WKRQebp_gabhggh_kh]eZr_gb| Как все
порядочные программы устаноdbijh]jZffZmklZgh\db3\WKRQ
показыZ_l лицензионное соглашение об использоZgbbijh]jZffgh]h
обеспечения. Python — это открытое программное обеспечение , и его
лицензия одобрена организацией Open Source Initiative . На протяжении
истории Python у него были разные владельцы и спонсоры, каждый из
которых остаbekой след ebp_gabbGhdhg_qgucj_amevlZllZdh\
исходный код Python о ткрыт, и его можно использоZlvgZex[hc
платформе, для любых целей, без платы и обязательст
Нажмите кнопку «Продолжить» («Continue») ещё раз.
5. Программа устаноdb3\WKRQ^bZeh]ijbgylbyebp_gabhggh]h
соглашения Из -за особенностей стандартного механизма устаноdb
Apple u^he`gukh]eZkblvkykebp_gab_cqlh[u\uihegblv
устаноdmIhkdhevdm3\WKRn — открытое программное обеспечение,
«согласие» с лицензией скорее расширяет ваши права, нежели
ограничиZ_lbo
Нажмите кнопку «Согласен» («Agree») для продо лжения.
6. Программа устаноdb3\WKRQklZg^ZjlgucwdjZgmklZghки Следующий
экран позhey_lbaf_gblvf_klhmklZghки. Python обязательно надо
устанаebать на системный диск, но из -за ограничений программы
устаноdbwlhg_ijhеряется. По пра^_]hоря, мне никогда не
приходилось изменять место устаноdb
В этом экране можно также уточнить список устанаebаемых
компонентоыбраbebbkdexqb некоторые из них. Если uohlbl_wlh
сделать, нажмите кнопку «Компоненты» («Customize»), в противном
случае нажмите «Устаноblv ,QVWDOO .
7. Программа устаноdb3\WKRQwdjZgыборочной устаноdb Если u
хотите произ_klbыборочную устаноdmijh]jZffZmklZghки покажет
следущий список компоненто
o Фреймhjd Python (Python Framework ). Это осноgZyqZklv
Python, она всегда u[jZgZbg_Zdlbна, потому что должна быть
обязательно устаноe_gZ.

11

o Графические приложения (GUI Applications ) dexqZxl,'/E —
графическую оболочку Python, которую u[m^_l_bkihevahать на
протяжении всей книги. Я настоятельно рекомендую остаblvw ту
опцию dexqzgghc.
o Инструменты командной строки UNIX (UNIX command -line tools )
dexqZxlijbeh`_gb_dhfZg^ghckljhdb . Эту опцию я тоже python3
gZklhyl_evghj_dhf_g^mxhklZblv
o Документация по Python (Python Documentation ) содержит
значительную часть инфо рмации с docs.python.org . Рекомендуется,
если у Zk dial -up или ограниченный доступ к Интернету.
o Инструмент обноe_gbyijhnbeyh[hehqdb (Shell profile
updater ) упраey_lh[ghлением Zr_]hijhnbeyh[hehqdb
(используе мого в «Terminal.app») и обеспечиZ_lgZoh`^_gb_
данной _jkbb3\WKRQ\imlyoihbkdZijh]jZff\Zr_ch[hehqdb [1].
Вероятно, Zfg_ihlj_[m_lkybaf_gylvwlhlimgdl.
o Опцию Испраblvkbkl_fguc3\WKRn (Fix system Python )
изменять не нужно. (Она застаey_lаш «мак» использоZlv
Python 3 как интерпреатор по умолчанию для всех скриптоgZ
Python, dexqZy\kljh_ggu_kbkl_fgu_kdjbiluhl$SSOH;m^_l
очень плохо, потому что большинстhkdjbilh написаны на
Python 2, и они перестанут праbevghjZ[hlZlvih^3\WKRn 3.)
Нажмите кнопку «Устаноblv ,QVWDOO ^eyijh^he`_gby.
8. Программ а устаноdb3\WKRQ^bZeh]h^ZiZjheyZ^fbgbkljZlhjw Для
того, чтобы устаноblvkbkl_fgu_nj_cfорки и библиотеки в
, программа устаноdbkijhkblmас пароль /usr/local/bin/
Z^fbgbkljZlhjZ;_aijbbe_]bcZ^fbgbkljZlhjZmklZghblv3\WKRQgZ
0DFg_evay
Нажмите кнопку «OK», чтобы начать устаноdm.
9. Программа устаноdb3\WKRQbg^bdZlhjijh]j_kkw Программа устаноdb
будет показыZlvbg^bdZlhjijh]j_kkZо j_fymklZghки u[jZgguo
компоненто.
10. Программа устаноdb3\WKRQmklZghка uihg_gw Если kzijhc^zl
праbevghijh]jZffZmklZghки покажет большую зелёную галку,
означающую, что устаноdZaZершена успешно.
Нажмите кнопку «Закрыть» («Close»), чтобы uclbbaijh]jZffu
устаноdb.
11. Содержимое папки Если ug_f_gyeb /Applications/Python 3.1/
f_klhmklZghdb k_`_mklZghe_ggu_nZceu[m^mljZkiheZ]Zlvky\

12

папке gmljbiZidb . Наиболее Z`gZy_z Python 3.1 /Applications
qZklv — IDLE, графическая оболочка Python.
ДZ`^udebdgbl_ih , чтобы запустить оболочку Python. IDLE
12. Графическая интерактивная оболочка Python на Mac Оболочка
Python — это то место, где вы проведёте бо
́льшую часть времени,
исследуя Python. Во всех примерах wlhcdgb]_ij_^iheZ]Z_lkyqlh
знаете, как найти оболочку Python.
Перейти к использованию оболочки Python .

Установка на Ubuntu Linux
Соj_f_ggu_ дистрибутиu/LQXx подкреплены обширными репозиториями
предкомпилироZgguoijbeh`_gbc( пакетов ), готовых к устаноd_Lhqgu_
с_^_gbyfh]mlhlebqZlvkyhl^bkljb[mlbа к дистрибутиву. В Ubuntu Linux
самый простой способ устаноblv3\WKRn 3 — через приложение
«УстаноdZm^Ze_gb_ $GG5HPRYH \f_gxIjbeh`_gby $SSOLFDWLRQV .
1.

УстаноdZm^Ze_gb_ijbeh`_gbyih^^_j`bаемые компанией Canonical
Когда uперu_aZimkdZ_l_ «Устаноdmm^Ze_gb| », отображается
список приложений по категориям. Некоторые из них уже устаноe_gu
но бо
́льшая часть — нет. Репозиторий содержит более 10 000
приложений, поэтому ufh`_l_ijbf_gblvjZaebqgu_nbevljuqlh[u
пр осмотреть меньшие части репозитория. Фильтр по умолчанию —
«Приложения, поддержиZ_fu_dhfiZgb_c&DQRQLFDO &DQRQLFDl -
maintained applications») — показыZ_lg_[hevrh_ih^fgh`_klо из
общего числа приложений, только те, что официально поддержиZxlky

13

компа нией Canonical , создающей и поддержиZxs_c8EXQWX/LQX[.

2.

УстаноdZm^Ze_gb_\k_2SHQ6RXUFHijbeh`_gby
Python 3 не поддержиZ_lky&DQRQLFDOihwlhfmkgZqZeZыберите из
uiZ^Zxs_]hf_gxnbevljh «Все Open Source приложения» («All Open
Source applicati ons»).

3.

УстаноdZm^Ze_gb_ihbkdS\WKRQ»
После переключения фильтра на отображение k_ohldjuluo
приложений сразу же hkihevamcl_kvkljhdhcihbkdZqlh[ugZclb
«python 3».

14

4.

УстаноdZm^Ze_gb_ыбор пакета Python 3.0
Теперь список приложений сокр атился до тех, которые соот_lklуют
запросу «python 3». Нужно отметить дZiZd_lZI_j\uc —
«Python (v3.0)». Он содержит собственно интерпретатор Python.

5.

УстаноdZm^Ze_gb_<u[hjiZd_lZ,'/(^ey3\WKRn 3.0
Второй пакет, который Zfgm`_ggZoh^blkyg епосредст_gghgZ^
перuf — «IDLE (using Python -3.0)». Это графическая оболочка Python,
которую u[m^_l_bkihevahать на протяжении всей книги.
После того, как uhlf_lbl_wlb^а пакета, нажмите кнопку «Применить
изменения» («Apply Changes») для продолж ения.

15

6.
УстаноdZm^Ze_gb_
применение изменений
Программа упраe_gby
пакетами попросит
подт_j^blvqlh
uohlbl_mklZgh\blv
дZiZd_lw — «IDLE
(using Python -3.0)» и
«Python (v3.0)».
Нажмите кнопку
«Применить» («Apply»)
для продолжения.

7.
УстаноdZm^Ze_gb_bg^bdZlhj
uiheg_gbyaZ]jmadb
Программа упраe_gbyiZd_lZfb
будет показыZlvbg^bdZlhj
uiheg_gby\hремя загрузки
необходимых пакетоba
Интернет -репозитория Canonical.

8.
УстаноdZm^Ze_gb_bg^bdZlhjыполнения устаноdb

16

После загру зки пакетоijh]jZffZmijZления пакетами аlhfZlbq_kdb
начнёт устанаebать их.

9.
УстаноdZm^Ze_gb_ghые приложения устаноe_gu
Если всё прошло хорошо, программа упраe_gbyiZd_lZfbih^lердит,
что оба пакета были успешно устаноe_guHlkx^Z\ufh`_l| запустить
оболочку Python, дZ`^udebdgm по пункту «IDLE», или, нажаdghidm
«Закрыть» («Close»), выйти из программы упраe_gbyiZd_lZfb
Вы всегда сможете запустить оболочку Python, из меню «Приложения»
(«Applications»), подменю «ПрограммироZgb_ («Programming»), u[jZ
пункт «IDLE».

17

10.

Графическая
интерактиgZy
оболочка
Python для
Linux
Оболочка
Python — это
то место, где
uijh\_^zl_
бо
́льшую часть
j_f_gb
исследуя
Python. Во k_o
примерах 
этой книге
предполагается, что знаете, как найти обол очку Python.

Перейти к использованию оболочки Python .
Установка на другие платформы
Python 3 доступен на множест_jZaghh[jZaguoieZlnhjf<qZklghklbhg
доступен почти в любом дистрибути_/LQX[ BSD и Solaris . Например, RedHat
Linux использует программу упраe_gbyiZd_lZfb yum ; у FreeBSD сhb порты и
коллекции пакетов ; у Solaris — pkgadd со сhbfb^jmavyfbIhbkd\ебе по
словам «Python 3» + назZgb_\Zr_chi_jZpbhgghckbkl_fu[ukljhihdZ`_l
имеется ли соот_lklующий пакет Python 3 и как его устано blv.
Использование оболочки Python
Оболочка Python — это то место, где можно исследовать синтаксис Python,
получать интерактиgmxkijZку по командам и отлажиZlvg_[hevrb_
программы. Графическая оболочка Python — IDLE — dexqZ_ldjhf_lh]h

18

неплохой тек стовый редактор, поддержиZxsbcih^kетку синтаксиса Python.
Если у ZkihdZg_lex[bfh]hl_dklhого редактора, стоит попробоZlv,'/(.
Перh -наперhkZfZihk_[_h[hehqdZ3\WKRn — замечательная
интерактиgZyiehsZ^dZ^eyb]jkyaudhfGZijhly`_gbb\k_c книги u
будете klj_qZlvijbf_jugZih^h[b_wlh]h:
>>> 1 + 1
2
Первые три углоuokdh[db — >>> — обозначают приглашение оболочки
Python. Его одить не надо. Это только для того, чтобы показать Zfqlhwlhl
пример должен uihegylvky\h[hehqd_3\WKRQ.
1 + 1 — это, то, что uводите. В оболочке ufh`_l__klbex[h_
корректное ujZ`_gb_bebdhfZg^myaudZ3\WKRQG_kl_kgycl_kvhgZg_
укусит! Худшее, что может случиться, — это сообщение об ошибке. Команды
uihegyxlkykjZam dZdlhevdh\ugZ`fzl_ ↵ ), выражения uqbkeyxlky Enter
lh`_g_f_^e_gghbh[hehqdZi_qZlZ_lj_amevlZl
2 — результат uqbke_gbywlh]hыражения. Как ожидалось, 1 + 1 яey_lky
корректным ujZ`_gb_fgZ3y thon. Результат, конечно же, 2.
Теперь попробуем другой пример.
>>> print ('Hello world!' )
Hello world !
Довольно просто, пра^Z"Gh\h[hehqd_3\WKRQfh`ghk^_eZlv]hjZa^h
больше разных _s_c?kebы где -нибудь застрянете, ^jm]aZ[m^_l_
команду или какие аргументы нужно передаZlvdZdhc -то функции, в оболочке
Python u\k_]^Zfh`_l_ызZlvbgl_jZdlbную спраdmIjhklh_^bl_ help
и нажмите ↵ . Enter

Переh^khh[s_gby оболочки
>>> help

Type help() for interactive
help, or help(object) for help
about object.
В_^bl_ help ()=для oh^Z режим интерактивной
спраdbbeb help Eобъект F=для получения спраdbh
конкретном объекте. =
Есть дZj_`bfZ\kljh_gghckijZки. Можно получить спраdmihdhgdj_lghfm
объекту, при этом просто u\_^_lky^hdmf_glZpbyb\uернётесь к
приглашению оболочки Python. Ещё можно hclb спраhqgucj_`bf
котором не uqbkeyxlkyыражения Python, а uijhk то одите ключеu_
слова и назZgbydhfZg^Z\hlет u\h^blkyсё, что из_klghh^Zgghc
команде.

19

Чтобы hclb интерактиguckijZ\hqgucj_`bfведите help () и нажмите ↵
. Enter

Переh^khh[s_gbch[hehqdb
>>> help ()

Welcome to Python 3.0! This is
the online help utility.
Добро пожаловать 3\WKRn =3.0! Вы
находитесь в режиме оперативной спраdb. =
If this is your first time using=
Python, you should definitely
check out =
the tutorial on the Internet at
http://docs.python.org/tutorial/K =
Если ubkihevam_l_3\WKRQпервые, Zf
определённо следует ознакомиться с
обучающим Интернет Jкурсом на
http://docs.python.org/tutorial/ [2].
Enter the name of any module,
keyword, or topic to get help on
writing
Python programs and using
Python modules. To quit this help
utility and
return to the interpreter, just type
"quit".
В_^bl_gZaание модуля, ключеh_keh\h
или тему, чтобы получить спраdmih
написанию программ на Python и
использоZgbx модулей. Чтобы выйти из
спраhqgh]hj_`bfZbернуться в
интерпретатор, просто едите quit K=
To get a list of available modules,
keywords, or topics, type
"modules", =
"keywords", or "topics".= =Each=
module also comes with a one J
line summary =
of what it does; =to list the modules
whose summaries contain a given
word =
such as "spam", type "modules
spam". =
Чтобы просмотреть список доступных
модулей, ключеuokeh\bl_fkijZ\db
едите modules , keywords =или topics . У
каждого модуля есть краткое описание его
назначе ния; чтобы получить список модулей,
hibkZgbbdhlhjuoстречается
определённое слоhgZijbf_jkehо
«spam», едите modules spam K=
help [=

Обратите внимание, что приглашение изменилось с >>> на help >. Это значит,
что вы находитесь j_`bf_bgl_jZdlbной спраdbA^_kv\ufh`_l_\ести
любое ключеh_keh\hdhfZg^mgZaание модуля или функции — что угодно,
что может понять Python — и прочитать документацию по нему.

20


Переh^khh[s_gbch[hehqdb
help > print 1.

Help on built -in function print
in module builtins:
СпраdZih\kljh_gghcnmgdpbb print из
модуля builtins :
print(...)

print(value, ..., sep=' ',
end=' \n', file=sys.stdout)
Prints the values to a stream,
or to sys.stdout by default.
Optional keyword arguments:
file: a file -like object (stream);
defaults to the current
sys.stdout.
sep: string inserted between
values, default a space.
end: string appended after
the last v alue, default a
newline.

Печатает значения mdZaZggucihlhdbeb
sys .stdout (по умолчанию).
Необязательные именоZggu_Zj]mf_glu:
 file — файлоподобный объект (поток), по
умолчанию sys .stdout ;
 sep — строка, встаey_fZyf_`^m
значениями, по умолчанию пробел;
 end — строка, дописыZ_fZyihke_
последнего значения, по умолчанию
симheghой строки .

help > PapayaWhip 2.

no Python documentation
found for 'PapayaWhip'
 Python не найдена документация по
«PapayaWhip» [3]


help > quit 3.

You are now leaving help and
returning to the Python
interpreter.
If you want to ask for help on
a particular object directly
from the
interpreter, you can type
"help(object)". Executing
"help('string')"
has the same effect as typing
a particular string at the help>

Вы покидаете режим спраdbbозвращаетесь
bgl_jij_lZlhj3\WKRQ?kebы хотите
получить спраdmhg_dhlhjhfh[t_dl_ijyfh
из интерпретатора, можете \_klb
help (объект ). Выполнение help ('строка' )
работает так же, как од этой строки 
приглашение help >.

21

prompt.


>>> 4.

1. Чтобы получить документацию по функции print (), просто едите print и
нажмите ↵ Enter . Интерактивная спраdZihdZ`_lg_qlhроде man -
страницы: имя функции, краткое описание, аргументы их значения по
умолчанию и так далее. Если документация u]ey^blg_hq_gvihgylgh
не пугайтесь. В ближайших глаZoы получите более полное
предстаe_gb_hx о kzfwlhf.
2. Конечно, интерактиgZykijZка не kzagZ_l?keb\u_^zl_qlh -то,
что не яey_lkydhfZg^hc3\WKRQfh^me_fnmgdpb_cbeb^jm]bf
kljh_ggufdexq_ым словом, интерактиgZykijZка лишь пожмёт
сhbfbиртуальными плечами.
3. Чтобы uclbbabgl_jZdlb\ghckijZ\db_^bl_ quit и нажмите ↵ Enter .
4. Приглашение сноZklZeh >>> , чтобы показать, что uышли из режима
интерактиghckijZки и _jgmebkv оболочку Python.
IDLE, графическая оболочка Python, dexqZ_l_szbl_dklhый ре дактор с
расц_ldhcdh^Z3\WKRQ.
Редакторы и IDE для Python
IDLE — не лучший ZjbZgldh]^Z^_eh^hoh^bl^hgZibkZgbyijh]jZffgZ
Python. Поскольку программироZgb_ihe_ag__gZqbgZlvbamqZlvkhkоения
самого языка, многие разработчики предпочитают другие текстоu_j_^Zdlhju
и интегрированные среды разработки (Inte grated Development Environment,
IDE). Я не буду здесь о них подробно рассказыZlvghkhh[s_klо Python
имеет список поддержиZxsbo3\WKRQj_^Zdlhjhy , покрыZxsbcrbjhdbcki_dlj
платформ и лицензий.
Вы также можете a]eygmlvgZ список IDE, поддержиZxsbo3\WKRn , правда, пока
немногие из них поддержиZxl3\WKRn 3. Один из них — PyDev , плагин для
Eclipse [1] , преjZsZxsbc_]h\iheghp_ggmxkj_^mjZajZ[hldbgZ3\WKRQB
Eclipse, и PyDev кроссплатформенные и открытые .
На коммерческом фронте есть Komodo IDE от ActiveState. Его нужно
лицензироZlv^eydZ`^h]hihevah\Zl_eyghklm^_glZf^Zxlkdb^dbZlZd`_
есть hafh`ghklv[_kieZlghhagZdhfblvkykijh^mdlhf течение
ограниченного периода.
Я пишу на Python девять лет, и делаю это  GNU Emacs [2] , а отлажиZx
оболочке Python в командной строке. В разработке на Python нет более

22

праbevguobebf_g__ijZильных способо>_eZcl_lhqlhkqblZ_l_
праbevguflhqlhjZ[hlZ_l^ey\Zk.
Примечания
1. Написано немного путано, но так оно и есть. По теме можно почитать
w:en:PATH (variable) . — Прим. пер.
2. Обучающий курс на английском языке. В Викиучебнике доступен его
переh^gZjmkkdbc — Учебник Python 3.1 . — Прим. пер.
3. Papaya whip ( англ. ) — мусс из папайи . — Прим. пер.

23

Ваша первая
программа на
PWKRn

Не убегайте от проблем, не осуждайте себя и не несите сhz[j_fy\
пра_^ghf[_afheии. У Zk_klvijh[e_fZ"Ij_djZkghWlhihc^zlgw =
пользу! Радуйтесь: погрузитесь g_zbbkke_^mcl_! =
Досточтимый Хенепола Гунаратана
Погружение
Обычно книги о программироZgbbgZqbgZxlkykdmqbkdmqguo]eZ о базоuo
_sZoihkl_i_gghi_j_oh^yl к созданию чего -нибудь полезного. ДаZcl_сё
это пропустим. Вот Zf]hlhая, работающая программа на Python. Возможно,
ujh\gufkqzlhfgbq_]h\g_cg_ihcfzl_G_[_kihdhcl_kvh[wlhfkdhjh
мы разберём её строчка за строчкой. Но сначала прочитайте код и посмотрите,
что вы сможете изe_qvbag_]h.
[humansize.py ]
SUFFIXES = {1000 : ['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ],
1024 : ['KiB' , 'MiB' , 'GiB' , 'TiB' , 'PiB' , 'EiB' , 'ZiB' , 'YiB' ]}

def approximate_size (size , a_kilobyte_is_1024_bytes =True ):
'''Преобразует размер файла m^h[hqblZ_fmx^eyq_eh\_dZnhjfm.

Ключеu_Zj]mf_glu:
size -- размер файла [ZclZo

24

a_kilobyte_is_1024_bytes -- если True (по умолчан ию), используются степени
1024
если False, используются степени 1000

ВозjZsZ_ll_dklhую строку (string)

'''
if size < 0:
raise ValueError ('число должно быть неотрицательным' )

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
for suffix in SUFFIXES [multiple ]:
size / = multiple
if size < multiple:
return '{0:.1f} {1}' .format (size , suffix )

raise ValueError ('число слишком большое' )

if __name__ == '__main__' :
print (approximate_size (1000000000000 , False ))
print (approximate_size (1000000000000 ))
Теперь давайте запустим эту программу из командной строки . В Windows это
будет u]ey^_lvijbf_jghlZd:
c: \home \diveintopython3 \examples> c: \python31 \python.exe humansize.py
1.0 TB
931.3 GiB
В Mac OS X и Linux, будет почти то же самое:
you@localhost:~/diveintopython3/examples$ python3 humansize.py
1.0 TB
931.3 GiB
Что сейчас произошло? Вы uihegbebkою перmxijh]jZffmgZ3\WKRQ<u
uaали интерпретатор Python в командной строке и передали ему имя
скрипта, который хотели uihegblv<kdjbil_hij_^_e_gZnmgdpby
approximate_size (), которая принимает точный размер файла [ZclZob
uqbkey_ldjZkbый» (но приблизительный) размер. (Возможно, uидели
это  Проh^gbd е Windows , 0Dc OS X Finder , в Nautilus , Dolphin или Thunar 
Linux. Если отобразить папку с документами в b^_lZ[ebpunZceh\uc
менеджер в каждой её строке покажет иконку, назZgb_^hdmf_glZjZaf_j
тип, дату последнего изменения и т. д. Если iZid_ есть 1093 -байтовый файл
с названием «TODO», файловый менеджер не покажет «TODO 1093 байта»;
f_klhwlh]hhgkdZ`_lqlh -то типа «TODO 1 КБ». Именно это и делает
функция approximate_size ().)

25

Посмотрите на последние строки скрипта, umидите дZызоZ
print (approximate_size (аргументы )). Это вызоunmgdpbcKgZqZeZызыZ_lky
approximate_size (), которой передаётся несколько аргументов, затем берётся
haращённое ею значение и передаётся прямо в функцию print (). Функция
print () встроенная, ugb]^_g_gZc^zl_ её явного объяe_gby?zfh`gh
только использоZlv]^_m]h^ghbdh]^Zm]h^gh ?klvfgh`_klо встроенных
функций, и ещё больше функций, которые u^_e_gu отдельные модули .
Терпение, непоседа.)
Итак, почему при uiheg_gbbkdjbilZ\dhfZg^ghckljhd_\k_]{ а получается
один и тот же результат? Мы ещё дойдём до этого. Но сначала даZcl_
посмотрим на функцию approximate_size ().
Объявление функций
В Python есть функции, как и в большинст_^jm]boyaudh, но нет ни
отдельных заголоhqguonZceh\dZd C++ , ни конструкций
/ , как  Паскале . Когда Zfgm`gZnmgdpbyijhklh interface implementation
h[tybl__zgZijbf_jlZd
def approximate_size (size , a_kilobyte_is_1024_bytes =True ):
Когда вам нужна функция, просто
объявите её.
Объяe_gb_gZqbgZ_lkykdexq_ого слова def , затем следует имя функции, а
за ним аргументы kdh[dZo?kebZj]mf_glh несколько, они разделяются
запятым и.
К тому же, стоит заметить, что h[tyлении функции не задаётся тип
haращаемых данных. Функции в Python не определяют тип возjZsZ_fuo
ими значений; они даже не указыZxlkms_klует ли haращаемое значение
hh[s_ GZkZfhf^_e_ex[Zynmgdpby\3\t hon haращает значение; если
nmgdpbbыполняется инструкция return , она haращает указанное wlhc
инструкции значение, если нет — haращает None — специальное нулеh_
значение.)

В некоторых языках программироZgbynmgdpbb озвращающие
значение) объяeyxlkydexq_ым словом , а подпрограммы (не function
haращающие значений) =— =ключевым словом . В Python же sub
подпрограмм нет. Все функции ha\jZsZxlagZq_gb_ ^Z`__kebhgh
None ), и k_]^Zh[tyляются ключевым словом def K=

26

Функция approximate_size () принимает дZZj]mf_glZ и size
ghgbh^bgbagbog_bf__llbiZ<3\WKRQlbi kilobyte_is_1024_bytes
i_j_f_gguogbdh]^Zg_aZ^Zzlkyy\gh3\WKRQuqbkey_llbii_j_f_gghcb
ke_^blaZ gbfkZfhklhyl_evgh

В Java =и других языках со статической типизацией необходимо указыZlv
тип ha\jZsZ_fh]hagZq_gbybdZ`^h]hZj]mf_glZnmgdpbb<3\WKRQyно
указыZlvlbi^eyq_]h Jлибо, не нужно. Python самостоятельно
отслежиZ_llbiui_j_f_gguogZh сно_ijbk\Zbаемых им значений. =
Необязательные и именованные аргументы
В Python аргументы функций могут иметь значения по умолчанию; если
функция uauается без аргумента, то он принимает сhzagZq_gb_ih
умолчанию. К тому же, аргументы можно указыZlvy любом порядке, задавая
их имена.
Давайте ещё раз посмотрим на объяe_gb_nmgdpbb approximate_size ():
def approximate_size (size , a_kilobyte_is_1024_bytes =True ):
Второй аргумент — — записыZ_lkykhagZq_gb_f a_kilobyte_is_1024_bytes
ihmfheqZgbx True . Это означает, что этот аргумент необязательный; можно
uaать функцию без него, а Python будет дейстhать так, как будто она
uaана с True в качест_\lhjh]hiZjZf_ljZ.
Теперь a]eyg_fgZihke_^gb_kljhdbkdjbilZ:
if __name__ == '__main__' :
print (approximate_size (1000000000000 , False )) ①
print (approximate_size (1000000000000 )) ②
① Функция approximate_size ()=uauается с дmfyZj]mf_glZfb<gmljb
функции approximate_size ()=переменная =будет a_kilobyte_is_1024_bytes
False , поскольку False =передаётся яghо втором аргументе. =
② Функция approximate_size ()=uauается только с одним аргументом. Но kz
ihjy^d_ihlhfmqlh\lhjhcZj]mf_glg_h[yaZl_e_gIhkdhevdmторой
аргумент не указан, он принимает значение по умолчанию True , как
определено в объяe_gbbnmgdpbb. =
А ещё можно передавать значения в функцию по имени.
>>> from humansize import approximate_size
>>> approximate_size(4000, a_kilobyte_is_1024_bytes=False) ①
'4.0 KB'
>>> approximate_size(size=4000, a_kilobyte_is_1024_bytes=False) ②
'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, size=4000) ③

27

'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, 4000) ④
File "", line 1
SyntaxError: non -keyword arg after keyword a rg
>>> approximate_size(size=4000, False) ⑤
File "", line 1
SyntaxError: non -keyword arg after keyword arg
Переh^khh[s_gbch[hehqdb:
Файл "", строка 1
SQWD[(UURUg_bf_ghанный аргумент после именоZggh]h
① Функция approximate_size ()=uauается со значением 4000 =в перhf
аргументе и False =Zj]mf_gl_ihbf_gb . (Он a_kilobyte_is_1024_bytes
стоит на lhjhff_kl_ghwlhg_ажно, как вы скоро уb^bl_) =
② Функция approximate_size ()=uauается со значением 4000 =в аргументе по
имени =и False =Zj]mf_gl_ihbf_gb . (Эти size a_kilobyte_is_1024_bytes
именоZggu_Zj]mf_gluklhyl\lhf`_ihjy^d_\dZdhfhgbi_j_qbke_gu
h[ty\e_gbbnmgdpbbghwlhlh`_g_ажно.) =
③ Функция approximate_size ()=uauается с False =в аргументе по имени
=и 4000 =Zj]mf_gl_ihbf_gb . (Видите? Я a_kilobyte_is_1024_bytes size
же гоhjbeqlhihjy^hdg_ажен.) =
④ Этот uah не работает, потому что за именоZggufZj]mf_glhfke_^m_l
неименоZgguc ihabpbhgguc ?kebqblZlvkibkhdZj]mf_glh слева
напраhlhdZdlhevdhстречается именоZggucZj]mf_gl\k_ke_^mxsb_
за ним аргументы тоже должны быть именоZggufb. =
⑤ Этот uah тоже не работает, по той же причине, что и предыдущий.
Удиbl_evgh"<_^vkgZqZeZi_j_^Zzlky 4000 =Zj]mf_gl_ihbf_gb , size
затем, «очеb^ghfh`ghh`b^Zlvqlh False =станет аргументом по имени
. Но 3\WKRQwlhg_jZ[hl ает. Раз есть a_kilobyte_is_1024_bytes
именоZggucZj]mf_gl\k_Zj]mf_glukijZа от него тоже должны быть
именоZggufb. =
Написание читаемого кода
Не буду растопыриZlvi_j_^ами пальцы и мучить длинной лекцией о
Z`ghklb^hdmf_glbjhания кода. Просто знайте, что код пишется один раз, а
читается многократно, и самый Z`gucqblZl_evашего кода — это вы сами,
через полгода после написания (т. е. всё уже забыто, и ^jm]ihgZ^h[behkv

28

что -то починить). В Python писать читаемый код просто. Используйте это его
преимущестhbq_j_aihe]h^Z\ukdZ`_l_fg_kiZkb[h.
Строки документации
Функции 3\WKRQfh`gh^hdmf_glbjhать, снабжая их строками документации
(англ. documentation string , сокращённо docstring). В нашей программе у
функции approximate_size () есть строка документации:
def approximate_size (size , a_kilobyte_is_1024_bytes =True ):
'''Преобразует размер файла m^h[hqblZ_fmx^eyq_eh\_dZnhjfm.

Ключеu_Zj]mf_glu:
size -- размер файла [ZclZo
a_kilobyte_is_1024_bytes -- если True (по умолчанию), используются степени
1024
если False, используются степени 1000

ВозjZsZ_ll_dklhую строку (string)

'''
Каждая функция заслуживает хорошую
документацию.
Тройные каuqdb [1] используются для задания строк [2] содержащих
многострочный текст. Всё, что находится между начальными и конечными
кавычками, яey_lkyqZklvxh^ghckljhdb^Zgguoключая переводы строк,
пробелы в начале каждой строки текста, и другие каuqdb<ufh`_l_
использоZlvbo]^_m]h^ghghqZs_ k_]h[m^_l_стречать их 
определениях строк документации.

Тройные каuqdb =— =это ещё и простой способ определить строку,
содержащую одинарные (апострофы) и дhcgu_dZ\uqdbih^h[gh qq /...L =
Perl =RK=
Всё, что находится ljhcguodZ\uqdZo, — это строка документации функции,
описыZxsZyqlh^_eZ_lwlZnmgdpbyKljhdZ^hdmf_glZpbb_kebhgZ_klv
должна начинать тело функции, т. е. находится на следующей строчке сразу
под объяe_gb_fnmgdpbbKljh]h]hоря, ug_h[yaZguibkZlv
документацию к каждой сh_c функции, но всегда желательно это делать. Я
знаю, что Zfm`_се уши прожужжали про документироZgb_dh^Zgh3\WKRQ
даёт Zf^hihegbl_evgucklbfme — строки документации доступны hремя
uiheg_gbydZdZljb[mlnmgdpbb.

29


Многие IDE для Python используют строки документации для отображения
контекстной справки, и когда вы набираете назZgb_nmgdpbb_z
документация пояey_lkyо всплывающей подсказке. Это может быть
не_jhylghihe_aghghwlh\k_]hebrvkljhdb^hdmf_glZpbbdhlhju_ы
сами пишете.
Путь по иска оператора import
Перед тем, как идти дальше, я хочу djZlp_jZkkdZaZlvhimlyoihbkdZ
библиотек. Когда uiulZ_l_kvbfihjlbjhать модуль (с помощью оператора
import ), Python ищет его g_kdhevdbof_klZo<qZklghklbhgbs_lо всех
директориях, перечисленных  sys .path . Это просто список, который можно
легко просматривать и изменять при помощи стандартных списочных методов.
(Вы узнаете больше о списках ]eZе Встро енные типы данных.)
>>> import sys ①
>>> sys .path ②
['',
'/usr/lib/python31.zip' ,
'/usr/lib/python3.1' ,
'/usr/lib/python3.1/plat -linux2@EXTRAMACHDEPPATH@' ,
'/usr/lib/python3.1/lib -dynload' ,
'/usr/lib/python3.1/dist -packages' ,
'/usr/local/lib/python3.1/dist -packages' ]
>>> sys ③

>>> sys .path .insert (0, '/home/mark/diveintopython3/examples' ) ④
>>> sys .path ⑤
['/home/mark/diveintopython3/examples' ,
'',
'/usr/lib/python31.zip' ,
'/usr/lib/python3.1' ,
'/usr/lib/python3.1/plat -linux2@EXTRAMACHDEPPATH@' ,
'/usr/lib/python3.1/lib -dynload' ,
'/usr/lib/python3.1/dist -packages' ,
'/usr/local/lib/python3.1/dist -packages' ]
① ИмпортироZgb_fh^mey sys =делает доступными k__]hnmgdpbbb
атрибуты. =
② sys .path — =список имён директорий, определяющий текущий путь поиска.
(Ваш будет u]ey^_lvbgZq_\aZисимости от Zr_chi_jZpbhgghc
системы, от используемой _jkbb3\WKRQbhllh]hdm^Zhg[ue
устаноe_g 3\WKRQ[m^_lbkdZlv этих директориях (aZ^Zgghfihjy{ ке)

30

файл с расширением «.py», имя которого соiZ^Z_lkl_fqlhы
пытаетесь импортироZlv.
③ Вообще -то я Zkh[fZgmebklbggh_iheh`_gb_^_eg_fgh]hkeh`g__
потому что не все модули лежат в файлах с расширением «.py».
Некоторые из них, как, например, мо дуль sys , яeyxlkyстроенными; они
iZygu\kZf3\WKRQ<kljh_ggu_fh^mebедут себя точно так же, как
обычные, но их исходный код недоступен, потому что они не были
написаны на Python! (Модуль sys написан на Си .)
④ Можно добаblvghую директорию в путь поиска, добаb имя директории
kibkhd sys .path , hремя uiheg_gby3\WKRQblh]^Z3\WKRQ[m^_l
просматриZlv_zgZjZне с остальными, как только вы попытаетесь
импортироZlvfh^mevGhый путь поиска будет дейстb телен в течение
k_]hk_ZgkZjZ[hlu3\WKRQ.
⑤ Выполнив команду sys .path .insert (0, ноucBimlv ), u\klZили ноmx
директорию на перh_f_klh список sys .path , и, следовательно, gZqZeh
пути поиска модулей. Почти k_]^Zbf_gghwlhам и нужно. В случае
конфликта имён (например, если Python постаey_lkykh2 -й _jkb_c
некоторой библиотеки, а uohlbl_bkihevahать _jkbx wlhlijbzf
гарантирует, что будут найдены и использоZguаши модули, а не те,
которые идут dhfie_dl_k3\WKRQ.
Всё является объек том
Если uдруг пропустили, я только что сказал, что функции в Python имеют
атрибуты, и эти атрибуты доступны hремя uiheg_gbyNmgdpbydZdbсё
остальное в Python, яey_lkyh[t_dlhf.
Запустите интерактиgmxh[hehqdm3\WKRQbihторяйте за мной:
>>> import humansize ①
>>> print (humansize. approximate_size (4096 , True )) ②
4.0 KiB
>>> print (humansize. approximate_size .__doc__ ) ③
Преобразует размер файла в удобочитаемую для чело_dZnhjfm.

Ключеu_Zj]mf_glu:
size -- размер файла [ZclZo
a_kilobyte_is_1024_bytes -- если True (по умолчанию ), используются степени
1024
если False , используются степени 1000

ВозjZsZ_ll_dklhую строку (string )

31

① Первая строчка импортирует программу dZq_klе модуля — humansize
фрагмента кода, который можно использоZlvbgl_jZdlbно или из другой
Python -программы. После того, как модуль был импортироZgfh`gh
обращаться ко всем его публичным функциям, классам и а трибутам.
Импорт применяется как в модулях, для доступа к функциональности
других модулей, так и в интерактивной оболочке Python. Это очень важная
идея, и u_szg_jZa\klj_lbl__zgZkljZgbpZowlhcdgb]b.
② Когда uohlbl_bkihevahать функцию, определё нную в импортироZgghf
модуле, нужно дописать к её имени назZgb_fh^meyLh_klvы не
можете использовать просто approximate_size , обязательно
humansize. approximate_size . Если вы использоZebdeZkku\ Java , то для
Zkwlh^he`gh[ulvagZdhfh.
③ Вместо того, чтобы ua\Zlvnmgdpbx dZdы, hafh`ghh`b^Zeb ы
запросили один из её атрибутов — __doc__ .

Оператор import в Python похож на require из Perl . После import в Python,
uh[jZsZ_l_kvdnmgdpbyffh^meydZd ; после require  модуль('!*3
Perl, для обращения к функциям модуля используется имя
. модуль::функция
Что такое объект?
В языке Python всё яey_lkyh[t_dlhfbmex[h]hh[t_dlZfh]ml[ulv
атрибуты и методы. Все функции имеют стандартный атрибут __doc__ ,
содержащий строку документации, определённую в исходном коде функции.
Модуль sys — тоже объект, имеющий (кроме прочего) атрибут под назZgb_f
path . И так далее.
Но мы так и не получили от_lgZ]eZный hijhkqlhlZdh_h[t_dl"JZagu_
языки программироw ния определяют «объект» по -разному. В одних
считается, что все объекты должны иметь атрибуты и методы. В других, что
объекты могут порождать подклассы. В Python определение ещё менее
чёткое. Некоторые объекты не имеют ни атрибутоgbf_lh^h\ohlybfh]eb
бы их иметь. Не все объекты порождают подклассы. Но kzyляется объектом
lhfkfuke_qlhfh`_l[ulvijbk\h_ghi_j_f_gghcbebi_j_^Zghnmgdpbb
качест_Zj]mf_glZ.
Возможно, вы встречали термин « объект перh]hdeZkkw » в других книгах о
программироZgbb . В Python функции — объекты перh]hdeZkkw . Функцию
можно передать dZq_klе аргумента другой функции. Модули — объекты
перh]hdeZkkw . Весь модуль целиком можно передать dZq_klе аргумента
функции. Классы — объекты перh]hdeZkkZbhl^_evgu_bowda_f пляры —
тоже объекты перh]hdeZkkZ.

32

Это очень Z`ghihwlhfmyihторю это, на случай если uijhimklbeb
перu_g_kdhevdhjZa всё в Python яey_lkyh[t_dlhf . Строки — это
объекты. Списки — объекты. Функции — объекты. Классы — объекты.
Экземпляры классов — объекты. И даже модули яeyxlkyh[t_dlZfb.
Отступы
Функции 3\WKRQg_bf_xlgbyных указаний begin и end , ни фигурных
скобок, которые бы показыZeb]^_dh^nmgdpbbgZqbgZ_lkyZ]^_
заканчивается. Разделители — только дh_lhqb_( :) и отступы самого кода.
def approximate_size (size , a_kilobyte_is_1024_bytes =True ): ①
if size < 0: ②
raise ValueError ('число должно быть неотрицательным' ) ③

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
for suffix in SUFFIXES [multiple ]: ⑤
size / = multiple
if size < multiple:
return '{0:.1f} {1}' .format (size , suffix )

raise ValueError ('число слишком большое' )
① Блоки кода определяются по их отступам. Под «блоками кода» я
подразумеZxnmgdpbb[ehdb if, циклы for =и while =и=т. =д. Увеличение
отступа начинает блок, а уменьшение =— =заканчиZ_lGbkdh[hdgb
ключеuokeh\WlhhagZqZ_lqlhijh[_eubf_xlажное значение, и их
количестhlh`_<wlhfijbf_j_dh^nmgdpbbhl[blq_lujvfyijh[_eZfb
Не обязательно здесь должны быть =именно четыре пробела, просто их
число должно быть постоянным. ПерZyстретиrZykykljhqdZ[_a
отступа будет означать конец функции. =
② За оператором if=должен следоZlv[ehddh^Z?keb\j_amevlZl_
uqbke_gbymkeh\gh]hыражения оно окажется истинным, то uihegblky
блок, u^_e_gguchlklmihf\ijhlbном случае произойдёт переход к
блоку else =(если он есть). Обратите gbfZgb_qlh\hdjm]ыражения скобки
не стоят. =
③ Эта строчка находится внутри блока if. Оператор raise =uauает
исключение (типа ValueError ), но только если size =Y=MK=
④ Это ещё не конец функции. Соk_fimklu_kljhdbg_kqblZxlkyHgbfh]ml
поukblvqblZ_fhklvdh^Zghg_fh]mlkem`blvjZa^_ebl_eyfb[ehdh
кода. Блок кода функции продолжается на следующей строке. =

33

⑤ Оператор цикла for тоже начинает блок кода. Блоки кода могут содерж ать
несколько строк, а именно — столько, сколько строк имеют такую же
_ebqbgmhlklmiZWlhlpbde for содержит три строки кода. Других
синтаксических конструкций для описания многострочных блокоdh^Zg_l
Просто делайте отступы, и будет ZfkqZklv_!
После того, как ui_j_[hj_l_нутренние протиhj_qbybijhедёте пару
ехидных аналогий с Фортраном , uih^jm`bl_kvkhlklmiZfbbgZqgzl_идеть
их преимущестZH^ghba]eZных преимуществ — то, что все программы на
Python u]ey^ylijbf_jghh^bgZdh\hihk кольку отступы — требоZgb_yaudZ
а не hijhkklbey;eZ]h^ZjywlhfmklZghится проще читать и понимать код
на Python, написанный другими людьми.

В Python используются симheuозjZlZdZj_ldb =для разделения
операторов, а также дh_lhqb_bhlklmiu^eyj азделения блоков кода. В
C+H =и Java =используются точки с запятой для разделения операторов и
фигурные скобки для блокоdh^Z. =
Исключения
Исключения ( англ. exceptions — нештатные, исключительные ситуации,
требующие специальной обработки) используются повсюду 3\WKRQ
Буквально каждый модуль klZg^Zjlghc[b[ebhl_d_3\WKRQbkihevam_lbo^Z
и сам Python uauает их hfgh]bokblmZpbyo<u_szg_h^ghdjZlgh
klj_lbl_bogZkljZgbpZowlhcdgb]b.
Что такое исключение? Обычно это ошибка, признак того, что ч то -то пошло не
так. (Не все исключения яeyxlkyhrb[dZfbghihdZwlhg_ажно.) В
некоторых языках программироZgbyijbgylhозвращать код ошибки,
который вы потом про_jy_l_ . В Python принято использоZlvbkdexq_gby
которые u обрабатыZ_l_ .
Когда проис ходит ошибка в оболочке Python, она u\h^bldh| -какие
подробности об исключении и о том, как это случилось, и всё. Это назыZ_lky
необработанным исключением. Когда это исключение было uaано, не
нашлось никакого программного кода, чтобы заметить его и обр аботать
должным образом, поэтому оно kieuehgZkZfucерхний уро_gv — 
оболочку Python, которая вы_eZg_fgh]hhleZ^hqghcbgnhjfZpbbb
успокоилась. В оболочке это не так уж страшно, однако если это произойдёт h
j_fyjZ[hlugZklhys_cijh]jZffulhся программа с грохотом упадёт,
если исключение не будет обработано. Может быть это то, что Zfgm`ghZ
может, и нет.

В отличие от Java , функции 3\WKRQg_kh^_j`Zlh[tyлений о том, какие
исключения они могут uauать. Вам решать, какие из hafh`guo

34

исключений необходимо отлавлиZlv.
Результат исключения — это не k_]^ZihegucdjZoijh]jZffuBkdexq_gby
можно обработать . Иногда исключения hagbdZxlb~ -за настоящих ошибок 
Zr_fdh^_ gZijbf_j^hklmidi_j_f_gghcdhlhjZyg_kms_klует), но
порой ис ключение — это нечто, что ufh`_l_ij_^\b^_lv?kebы
открыZ_l_nZcehgfh`_lg_kms_klоZlv?kebы импортируете модуль,
он может быть не устаноe_g?kebы подключаетесь к базе данных, она
может быть недоступна или у Zkfh`_l[ulvg_^hklZlhqghij а^ey^hklmiZd
ней. Если uagZ_l_qlhdZdZy -то строка кода может вызZlvbkdexq_gb_lh
его следует обработать с помощью блока try ... except .

Python использует блоки try ..K except =для обработки исключений и оператор
raise =для их генерации. Java =и C+H =используют блоки try ..K catch =для
обработки исключений и оператор throw =для их генерации. =
Функция approximate_size () uauает исключение в двух разных случаях: если
переданный ей размер ( size ) больше, чем функция может обработать, или
если он меньше нуля.
if size < 0:
raise ValueError ('число должно быть неотрицательным' )
Синтаксис uahа исключений достаточно прост. Надо написать оператор
raise , за ним название исключения и опционально, поясняющую строку для
отладки. Синтаксис напоминает вызоnmgdpbb GZkZfhf^_e_bkdexq_gby
реализоZgudZddeZkkubhi_jZlhj raise просто создаёт экземпляр класса
ValueError и передаёт в его метод инициализац ии строку 'число должно быть
неотрицательным' . Но мы забегаем i_jz^)

Нет необходимости обрабатыZlvbkdexq_gb_\lhcnmgdpbbdhlhjZy_]h
uaала. Если одна функция не обработает его, исключение передаётся 
функцию, uaавшую эту, затем nmgdpbxызZ\rmxызZшую, и
т. =д. «ерх по стеку». Если исключение нигде не буде т обработано, то
программа упадёт, а Python uедет «раскрутку стека» ( англ. =traceback ) в
стандартный поток ошибок =— =и на этом конец. Повторяю, возможно, это
именно то, что Zfgm`gh, =— =это заbkblhllh]hqlh^_eZ_lаша
программа. =
Отлов ошибок импорта
Одно из встроенных исключений Python — ImportError (ошибка импорта),
которое uau\Z_lky_kebg_m^Zzlkybfihjlbjhать модуль. Это может
случиться по нескольким причинам, самая простая из которых — отсутстb_
модуля  пути поиска, оператора import . Что можно использоZlv^eyключен ия
ijh]jZffmhipbhgZevguoозможностей. Например, библиотека chardet

35

предостаey_l\hafh`ghklvZ\lhfZlbq_kdh]hhij_^_e_gbydh^bjhки
симheh. Предположим, ZrZijh]jZffZohq_lbkihevahать эту библиотеку
lhfkemqZ__kebhgZ_klvbebkihdhcghijh^he} ить работу, если
пользоZl_evg_mklZghил её. Можно сделать это с помощью блока
try ... except .
try :
import chardet
except ImportError :
chardet = None
После этого можно про_jylvgZebqb_fh^mey простым if: chardet
if chardet:
# что -то сделать
else :
# продолжить дальше
Другое частое применение исключения ImportError — выбор из дmofh^me_c
предостаeyxsboh^bgZdh\ucbgl_jn_ck( API ), причём применение одного из
них предпочтительнее другого (может, он быстрее работает или требует
меньше памяти). Для этого можно попытаться импортироZlvkgZqZeZh^bg
модуль, и если это не удалось , то импортироZlv^jm]hcDijbf_jm главе
XML рассказыZ_lkyh^ух модулях, реализующих один и тот же API, так
назыZ_fuc . Перuc — lxml — сторонний модуль, который ElementTree API
g_h[oh^bfhkdZqbZlvbmklZgZebZlvkZfhklhyl_evgh<lhjhc —
xml .etree .ElementTree — медленнее, но oh^bl\klZg^Zjlgmx[b[ebhl_dm
Python 3.
try :
from lxml import etree
except ImportError :
import xml .etree .ElementTree as etree
При uiheg_gbbwlh]h[ehdZ try ... except будет импортироZgh^bgba^ух
модулей под именем etree . Поскольку оба модуля реализуют один и тот же
API, то в последующем коде нет необходимости про_jylvdZdhcbawlbo
модулей был импортироZg И раз импортироZggucfh^mev любом случае
именуется как etree , то не придётся klZлять лишние if для обращения к
разноимённым модулям.
Несвязанные переменные
Взглянем ещё раз на hlwlmkljhqdmnmgdpbb approximate_size ():
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
Мы нигде не объяeyebi_j_f_ggmx multiple (множитель), мы только
присhbeb_cagZq_gb_<kz\ihjy^d_3\WKRQihaоляет так делать. Что он не

36

позheblk^_eZlvlZdwlhh[jZlblvkydi_j_f_gghcdhlhjhcg_[ueh
присh_gh~ начение. Если попытаться так сделать, hagbdg_lbkdexq_gb_
NameError (ошибка bf_gb .
>>> x
Traceback (most recent call last):
File "", line 1, in
NameError: name 'x' is not defined
>>> x = 1
>>> x
1
Переh^khh[s_gbyh[hehqdb:
Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
NameError: имя 'x' не определено
Однажды вы скажете Python «спасибо» за это.
Всё чувствительно к регистру
Все имена 3\WKRQqmстbl_evgudj_]bkljm — имена переменных, функций,
классов, модулей, исключений. Всё, что можно прочитать, записать, uaать,
создать или импортироZlvqmствительно к регистру.
>>> an_integer = 1
>>> an_integer
1
>>> AN_INTEGER
Traceback (most recent call last):
File "", line 1, in
NameError: name 'AN_INTEGER' is not defined
>>> An_Integer
Traceback (most recent call last):
File "", line 1, in
NameError: name 'An_Integer' is not defined
>>> an_inteGer
Traceback (most recent call last):
File "", line 1, in
NameError: name 'an_inteGer' is not defined
Переh^khh[s_gbch[hehqdb:
Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
NameError: имя '<имя>' не определено

37

И так далее.
Запуск скриптов
В PWKRQ.-- -( .
Модули Python — это объекты, имеющие несколько полезных атрибутоBwlh
обстоятельств о можно использоZlv^eyijhklh]hl_klbjhания модулей, при
их написании, путём dexq_gbyhkh[h]h[ehdZdh^Zdhlhjuc[m^_l
исполняться при запуске файла из командной строки. Взгляните на последние
строки : humansize.py
if __name__ == '__main__' :
print (approximate_size (1000000000000 , False ))
print (approximate_size (1000000000000 ))

Как и в C , 3\WKRQbkihevam_lkyhi_jZlhj == =для проверки на ра_gklо и
оператор Z=для присZbания. Но в отличие от C, Python не поддержиZ_l
присZbание gmljb^jm]h]h\ujZ`_gbyihwlhfmmас не получится
случайно присhblvagZq_gb_место про_jdbgZjZ\ghklv. =
Итак, что же делает этот блок if особенным? У k_ofh^me_cdZdmh[t_d то
есть встроенный атрибут __name__ (имя). И значение этого атрибута зависит
от того, как модуль используется. Если модуль импортируется, то __name__
принимает значение раgh_bf_gbnZceZfh^mey[_ajZkrbj_gbybimlbd
каталогу.
>>> import humansize
>>> humansize.__name__
'humansize'
Но модуль можно запустить и напрямую, как самостоятельную программу, 
этом случае __name__ примет особое значение — __main__ . Python uqbkebl
значение условного ujZ`_gby\hi_jZlhj_ if, определит его истинность, и
uihegbl[ehddh^Z if. В данном случае, будут напечатаны дZagZq_gby.
c: \home \diveintopython3> c: \python31 \python.exe humansize.py
1.0 TB
931.3 GiB
И это ваша перZyijh]jZffZgZ3\WKRQ!
Материалы для дальнейшего чтения
 PEP 257: Docstring Conventions объясняет, чем отличается хорошая строка
документации от великолепной.

38

 Pyt hon Tutorial: Documentation Strings также касается данного hijhkZ.
 PEP 8: Style Guide for Python Code обсуждает хороший стиль расстаноdb
отступо.
 Python Reference Manual объясняет, что означают слова « в Python всё
является объектом », потому что некоторые люди — педанты , которые
любят длиннющие обсуждения _s_clZdh]hjh^Z.
Примечания
1. В английском языке апострофы, обрамляющие текст, — это уже
одинарные кавычки. — Прим. пер.
2. Имеется иду тип данных string (строка). — Прим. пер.

39

Встроенные
типы данных

В начале всяческой философии лежит удиe_gb_bamq_gb_^ижет его
i_jz^g_ежестhm[bает его. =
Мишель де Монтень
Погружение
Отложите на минутку Zrm первую программу на Python и давайте поговорим о
типах данных. В Python у каждого значения есть тип, но нет необходимости
яghmdZauать типы переменных. Как это работает? Осноu\ZykvgZi_jом
присh_gbbagZq_gbyi_j_f_gghc3\WKRQhij_^_ey_l_zlbib\{ альнейшем
отслежиZ_l_]hkZfhklhyl_evgh.
В Python имеется множестh\kljh_gguolbih данных. Вот наиболее важные
из них:
1. Логический , может принимать одно из дmoagZq_gbc — True (истина)
или False (ложь).
2. Числа , могут быть целыми ( 1 и 2), с плавающей точкой ( 1.1 и 1.2 ),
дробными ( 1/2 и 2/3), и даже комплексными .
3. Строки — последоZl_evghklbkbfоло Юникода , например, HTML -
документ.
4. Байты и массивы байтов , например, файл изображения в формате
JPEG .
5. Списки — упорядоченные последовательности значений.
6. Кор тежи — упорядоченные неизменяемые последовательности
значений.
7. МножестZ — неупорядоченные наборы значений.
8. СлоZjb — неупорядоченные наборы пар b^Zdexq -значение.

40

Конечно, существуют и многие другие типы данных. В языке Python всё
яey_lkyh[t_dlhfihwl ому gzfbf_xlkylZd`_blZdb_lbiudZd модуль ,
функция , класс , метод , файл , и даже скомпилироZggucdh^ . Некоторые из
них um`_стречали: у модулей есть имена, функции имеют строки
документации, и т. д. С классами uihagZdhfbl_kv\]eZ\_DeZkkub
итераторы»; с файлами — в гла_NZceu.
Строки и байты насколько сложны, настолько же и Z`guihwlhfmbf
от_^_gZhl^_evgZy]eZа. Но сначала даZcl_ihagZdhfbfkykhklZevgufb
типами.
Логические значения
Практически любое выражение можно
использовать в логическом контексте.
Логический тип данных может принимать одно из дmoagZq_gbcbklbgZbeb
ложь. В Python имеются дв е константы с понятными именами True (от англ.
true — истина) и False (от англ. false — ложь), которые можно использоZlv
для непосредст_ggh]hijbkоения логических значений. Результатом
uqbke_gbyыражений также может быть логическое значение. В
определенных местах (например, в операторе if), Python ожидает, что
результатом uqbke_gbyy ыражения будет логическое значение. Такие места
назыZxl логическим контекстом . Практически любое ujZ`_gb_fh`gh
использоZlv логическом контексте, Python ex[hfkemqZ_ihiulZ_lky
определить его истинность. Для этого имеются отдельные наборы праbe^e я
различных типов данных, указыZxsb_gZlhdZdb_baboagZq_gbckqblZlv
истинными, а какие ложными eh]bq_kdhfdhgl_dkl_ WlZb^_yklZg_l[he__
понятна по мере ознакомления с конкретными примерами далее в этой главе.)
К примеру, рассмотрим следующий от рыhdbaijh]jZffuKXPDQVL]HS\:
if size < 0:
raise ValueError ('число должно быть неотрицательным' )
Здесь переменная size и значение 0 имеют тип целого числа, а знак < между
ними яey_lkyqbkeh\ufhi_jZlhjhfJ_amevlZlhf`_ычисления ujZ`_gby
size < 0 всегда будет логическое значение. Вы можете самостоятельно wlhf
убедиться с помощью интерактивной оболочки Python:
>>> size = 1
>>> size < 0
False
>>> size = 0
>>> size < 0
False

41

>>> size = -1
>>> size < 0
True
Из -за некоторых обстоятельств, сyaZgguokgZke_^b_fhklZшимся от
Python 2, логические значения могут трактоZlvkydZdqbkeZ True как 1, и False
как 0.
>>> True + True
2
>>> True - False
1
>>> True * False
0
>>> True / False
Traceback (most recent call l ast):
File "", line 1, in
ZeroDivisionError: int division or modulo by zero
Переh^khh[s_gbyh[hehqdb:
Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
ZeroDivisionError: целочисленное деление на ноль или
остаток по модулю ноль
Ой -ой -ой! Не делайте так! Забудьте даже, что я упоминал об этом.
Числа
Числа — это потрясающая штука. Их так много, k_]^Z_klvbaq_]hыбрать.
Python поддержиZ_ldZdp_eu_qbkeZlZdbk плавающей точкой . И нет
необходимости объ яeylvlbi^eybojZaebqby3\WKRQhij_^_ey_l_]hih
наличию или отсутстbx^_kylbqghclhqdb.
>>> type (1) ①

>>> isinstance (1, int ) ②
True
>>> 1 + 1 ③
2
>>> 1 + 1.0 ④
2.0
>>> type (2.0 )

① Можно использовать функцию type ()=для про_jdblbiZex[h]hagZq_gby

42

или переменной. Как и ожидалось, число 1 имеет тип int (целое).
② Функцию isinstance () тоже можно использоZlv^eyijhерки
принадлежности значения или переменной определенному типу.
③ Сложение дmoagZq_gbclbiZ int дает в результате тот же int .
④ Сложение значений типа int и float дает в результате float . Для uiheg_gby
операции сложения Python преобразует значение типа int в значение типа
float , и в результате haращает float .
Преобразование целых чисел в десятичные дроби и наоборот
Как ulhevdhqlhидели, некоторые операции (как, например, сложение), при
необходимости, преобразуют целые числа в числа с плавающей точкой. Это
преобразоZgb_fh`ghыполнить и самостоятельно.
>>> float (2) ①
2.0
>>> int (2.0 ) ②
2
>>> int (2.5 ) ③
2
>>> int (-2.5 ) ④
-2
>>> 1.12345678901234567890 ⑤
1.1234567890123457
>>> type (1000000000000000 ) ⑥

① Можно явно преобразоZlvagZq_gb_lbiZ int =в тип float , uaаnmgdpbx
float ()K=
② Так же нет ничего удиbl_evgh]h том, что можно преобразоZlvagZq_gb_
типа float =в значение типа int , с помощью функции int ()K=
③ Функция int ()=отбрасыZ_l^jh[gmxqZklvqbkeZZg_hdjm]ey_l_]h. =
④ Функция int ()=«округляет» отрицательные числа в сторону у_ebq_gbyHgZ
не haращает целую часть числа, как делает функция « пол » ( англ. =floor ), а
просто отбрасыZ_l^jh[gmxqZklv. =
⑤ Точность чисел с плавающей точкой раgZ^_kylbqgufagZdZf\
дробной части. =
⑥ Целые числа могут быть сколь угодно большими. =

43


Python 2 имел отдельные типы целых чисел: int и long . Тип int был
ограничен значением sys .maxint , которое менялось в заbkbfhklbhl
платформы, но обычно было раgh2 32−1. Python 3 же имеет только один
целочисленный тип, который в большинст_kemqZ_ _^zlk_[ydZdlbi
long в Python 2. См. PEP 237 .
Основные операции с числами
Над числами можно uihegylvjZaebqgu_ операции.
>>> 11 / 2 ①
5.5
>>> 11 // 2 ②
5
>>> −11 // 2 ③
−6
>>> 11.0 // 2 ④
5.0
>>> 11 ** 2 ⑤
121
>>> 11 % 2 ⑥
1
① Оператор L=uihegy_l^_e_gb_qbk_ekieZающей точкой. Он haращает
значение типа float , даже если и делимое, и делитель, имеют тип int K=
② Оператор //=uihegy_lp_ehqbke_ggh_^_e_gb_g_h[uqgh]hида. Когда
результат положительный, можете считать, что он просто отбрасыZ_l g_
округляет) дробную часть, но будьте осторожны с этим. =
③ Когда uihegy_lkyp_ehqbke_ggh_^_e_gb_hljbpZl_evguoqbk_e
оператор //=округляет результат до ближайшего целого [hevrmx
сторону. С математической точки зрения, это конечно же округление в
меньшую сторону, т. =к. −6 меньше чем −5; но это может сбить w с с толку, и
u[m^_l_h`b^Zlvqlhj_amevlZl[m^_lhdjm]ezg^h. =
④ Оператор //=не k_]^ZозjZsZ_lp_eh_qbkeh?kebohly[uh^bgba
операндов =— =делимое или делитель =— =будет типа float , то хотя результат
и будет округлён до ближайшего целого, ^_ck тbl_evghklbhglZd`_
будет иметь тип float K=
⑤ Оператор ** =uihegy_lоз_^_gb_ степень. 11 2 — =это 121. =
⑥ Оператор B =haращает остаток от целочисленного деления. 11, делённое
на 2, даёт 5 и 1 hklZld_ihwlhfma^_kvj_amevlZl =— =1. =

44


В Python 2, оператор / обычно означает целочисленное деление, но
добаb dh^ki_pbZevgmx^bj_dlbу можно застаblv_]hыполнять
деление с плавающей точкой. В Python 3, оператор / всегда означает
деление с плавающей точкой. См. PEP 238 .
Дроби
Python не ограничен целыми числами и числами с плаZxs_clhqdhcHg
также может выполнять k_l_aZ[Z\gu_\_sbdhlhju_ы изучали rdhe_gZ
уроках математики и затем благополучно забыли.
>>> import fractions ①
>>> x = fractions.Fraction(1, 3) ②
>>> x
Fraction(1, 3)
>>> x * 2 ③
Fraction(2, 3)
>>> fractions.Fraction(6, 4) ④
Fraction(3, 2)
>>> fractions.Fraction(0, 0) ⑤
Traceback (most recent call last):
File "< stdin>", line 1, in
File "fractions.py", line 96, in __new__
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)
Переh^khh[s_gbyh[hehqdb:
Раскрутка стека (список последних uahо :
Файл "", с трока 1, fh^mev>
Файл "fractions.py", строка 96, в __new__
raise ZeroDivisionError(' Дробь (%s, 0)' % числитель )
ZeroDivisionError: Дробь(0, 0)
① Перед началом использоZgby^jh[_cbfihjlbjmcl_fh^mev fractions K=
② Чтобы определить дробь, создайте объект класса Fraction =и передайте ему
числитель и знаменатель. =
③ С дробями можно uihegylvсе обычные математические операции. Все
они haращают ноuch[t_dldeZkkZ Fraction . =

45


Объект Fraction автоматически сократит дроби.
⑤ У Python хZlZ_la^jZ\h]hkfukeZqlh[ug_kha^Z\Zlv^jh[bkgme_\uf
знаменателем.
Тригонометрия
Ещё 3\WKRQfh`ghjZ[hlZlvkhkghными тригонометрическими функциями .
>>> import math
>>> math .pi ①
3.1415926535897931
>>> math .sin (math .pi / 2) ②
1.0
>>> math .tan (math .pi / 4) ③
0.99999999999999989
① Модуль math =содержит константу π =— =отношение длины окружности к её
диаметру. =
② Модуль math =содержит все осноgu_ljb]hghf_ljbq_kdb_nmgdpbb
dexqZy sin (), cos (), tan (), и их ZjbZglugZih^h[b_ asin ()K=
③ Заметьте, однако, что точность расчето\3\WKRQg_[_kdhg_qgZ
Выражение должно haращать значение 1.M , а не
0.9999999999999998V K=
Числа в логическом контексте
Нулевые значения — ложь, ненулевые
значения — истина.
Вы можете использоZlvqbkeZ\ логическом контексте , например, hi_jZlhj_
if. Нулевые значения — ложь, ненулеu_agZq_gby — истина.
>>> def is _it_true (anything ): ①
... if anything:
... print ("да , это истина ")
... else :
... print ("нет , это ложь ")
...
>>> is_it_true (1) ②
да , это истина
>>> is_it_true (-1)

46

да , это истина
>>> is_it_true (0)
нет , это ложь
>>> is_it_true (0.1 ) ③
да , это истина
>>> is_it_true (0.0 )
нет , это ложь
>>> import fractions
>>> is_it_true (fractions. Fraction (1, 2)) ④
да , это истина
>>> is_it_true (fractions. Fraction (0, 1))
нет , это ложь
① Вы знали, что можно определять сhbkh[klенные функции в
интерактиghch[hehqd_3\WKRQ"IjhklhgZ`bfZcl_deZ\brm = ↵ Enter 
конце каждой строки, а чтобы закончить од нажмите клаbrm = ↵ Enter на
пустой строке. =
② В логическом контексте, ненулевые целые числа =— =истина; значение M=— =
ложь. =
③ Ненулевые числа с плавающей точкой =— =истина; значение 0.M =— =ложь.
Будьте осторожны с этим! Если имеется малейшая ошибка округления (как
ufh]ebидеть в предыдущем разделе,э то iheg_\hafh`gh lh3\WKRQ
будет про_jylvagZq_gb_ 0.000000000000N =f_klh 0.M =и соот_lkl\_ggh
_jgzleh]bq_kdh_agZq_gb_ True K=
④ Дроби тоже могут быть использоZgu логическом контексте. Fraction EMI=
nF=— =ложь для k_oagZq_gbc n. Все остальные дроби =— =истина. =
Списки
Списки — рабочая лошадка Python. Когда я гоhjxkibkhdы, на_jgh_
думаете: «это массиq_cjZaf_jy^he`_gaZ^ZlvaZjZg__bdhlhjucfh`_l
хранить элементы только одного типа» и т. п., но это не так. Списки намного
интереснее.

Списки 3\WKRQihoh`bgZfZkkbы  Perl 5 . Там переменные,
содержащие массиu\k_]^ZgZqbgZxlkykkbfола ] ; 3\WKRQ
переменные могут назыZlvkydZdm]h^ghyaudke_^blaZlbihf
самостоятельно. =

47


В Python список — это нечто большее, чем массив  Java (хотя список
можно использоZlvbdZdfZkkb, если это дейстbl_evghlhq_]hы
хотите от жизни). Точнее будет аналогия с Java -классом ArrayList , который
может хранить произhevgu_h[t_dlub^bgZfbq_kdbjZkrbjylvkyih
мере добаe_gbyghых элементо.
Создание списка
Создать список легко: ibrbl_се значения, через запятую, dадратных
скобках.
>>> a_list = ['a' , 'b' , 'mpilgrim' , 'z' , 'example' ] ①
>>> a_list
['a' , 'b' , 'mpilgrim' , 'z' , 'example' ]
>>> a_list [0] ②
'a'
>>> a_list [4] ③
'example'
>>> a_list [-1] ④
'example'
>>> a_list [-3] ⑤
'mpilgrim'
① Сначала вы определили список из пяти элементов. Обратите gbfZgb_
они сохраняют свой перhgZqZevgucihjy^hdWlhg_kemqZcghKibkhd =— =
это упорядоченный набор элементо. =
② Список можно использоZlvdZdfZkkb с нумерацией от нуля. Первый
элемент не пустого списка будет всегда a_list xMzK=
③ Последним элементом этого пятиэлементного списка будет a_list xQz, потому
что нумерация элементов kibkd_сегда начинается с нуля. =
④ Используя отрицательный индекс, можно обратиться к элементам по их
номеру от конца списка. Последний элемент не пустого списка будет
k_]^Z a_list xJNzK=
⑤ Если Zkk[bают с толку отрицательные индексы, то просто думайте о них
следующим образом: a_list xJnz=== =a_list xlen Ea_list F=J=nz. В нашем примере
a_list xJPz=== =a_list xR=J=Pz=== =a_list xOzK=
Разрезание списка
a_list[0] — первый элемент списка a_list .

48

После того, ка к список создан, можно получить любую его часть в b^_kibkdZ
Это назыZ_lvky «slicing» — срез списка .
>>> a_list
['a' , 'b' , 'mpilgrim' , 'z' , 'example' ]
>>> a_list [1:3] ①
['b' , 'mpilgrim' ]
>>> a_list [1:-1] ②
['b' , 'mpilgrim' , 'z' ]
>>> a_list [0:3] ③
['a' , 'b' , 'mpilgrim' ]
>>> a_list [:3] ④
['a' , 'b' , 'mpilgrim' ]
>>> a_list [3:] ⑤
['z' , 'example' ]
>>> a_list [:] ⑥
['a' , 'b' , 'mpilgrim' , 'z' , 'example' ]
① Вы можете получить часть списка, назыZ_fmxkj_ahfmdZaZ дZ
индекса. В результате получается ноuckibkhdключающий в себя
элементы исходного в том же порядке, начиная с перh]hbg^_dkZkj_aZ 
данном случае a_list xNz), до последнего, но =не dexqZy_]h \^Zgghf
случае a_list xPz).=
② Срез работает, даже если один или оба индекса отрицательны. Если Zf
это поможет, можете думать об этом так: список читается слева направо,
перucbg^_dkkj_aZhij_^_ey_li_jый нужный Zfwe_f_glZ\lhjhc
индекс определяет перucwe_f_gldhlhjucам не нужен. ВозjZsZ_fh_
значение k_]^ZgZoh^blkyf_`^mgbfb. =
③ Нумерация спискоgZqbgZ_lkykgmeyihwlhfm a_list xMWPz=haращает
перu_ljbwe_f_glZkibkdZgZqbgZyk a_list xMz, заканчиZygZ ghg_
dexqZy a_list xPzK=
④ Если левый индекс среза =— =0, ufh`_l_himklblv_]h[m^_l
подразумеZlvkyLZd a_list xWPz=— =это то же самое,что и a_list xMWPz, потому
что начальный 0 подразумеZ_lky. =
⑤ Аналогично, если праucbg^_dkkj_aZyляется длиной списка, ufh`_l_
его опустить. Так, a_list xPWz=— =это то же самое, что и a_list xPWRz, потому что
этот список содержит пять элементоA^_kvijhke_`bается яgZy
симметрия. В этом пятиэлементном списке a_list xWPz=haращает перu_
элемента, а a_list xPWz=haращает последние дZwe_f_glZGZkZfhf^_e_
a_list x:n z=всегда будет haращать перu_Qwe_f_glh, а a_list xn: z=будет
haращать все остальные, незаbkbfhhl^ebgukibkdZ. =

49

⑥ Если оба индекса спис ка опущены, включаются k_we_f_glukibkdZGh
это не то же самое, что перhgZqZevgZyi_j_f_ggZy a_list . Это ноuc
список, dexqZxsbcсе элементы исходного. Запись a_list [:] предстаey_l
собой простейший способ получения полной копии списка.
Добавление элементов в список
Сущестm_lq_luj_kihkh[Z^h[Zить элементы в список.
>>> a_list = ['a' ]
>>> a_list = a_list + [2.0 , 3] ①
>>> a_list ②
['a' , 2.0 , 3]
>>> a_list. append (True ) ③
>>> a_list
['a' , 2.0 , 3, True ]
>>> a_list. extend (['four' , 'Ω ']) ④
>>> a_list
['a' , 2.0 , 3, True , 'four' , 'Ω ']
>>> a_list. insert (0, 'Ω ') ⑤
>>> a_list
['Ω ', 'a' , 2.0 , 3, True , 'four' , 'Ω ']
① Оператор H=соединяет списки, создаZyghый список. Список может
содержать любое число элементов; ограничений размера не сущестm_l
(пока есть доступная память). Однако, если вы заботитесь о памяти,
знайте, что сложение списков создает ещё один список iZfylb<^w нном
случае, этот ноuckibkhdg_f_^e_gghijbkаиZ_lkykms_klующей
переменной a_list . Так что эта строка кода, на самом деле, реализует
дmowlZigucijhp_kk =— =сложение, а затем присh_gb| =— =который может
(j_f_ggh ihlj_[hать много памяти, если ubf__l| =дело с большими
списками. =
② Список может содержать элементы любых типоbwe_f_gluh^gh]h
списка не обязательно должны быть одного и того же типа. Здесь мы
b^bfkibkhdkh^_j`ZsbckljhdmqbkehkieZ\Zxs_clhqdhcbp_eh_
число. =
③ Метод append ()=добаe яет один элемент dhg_pkibkdZ L_i_jvmgZk\
списке присутстmxl четыре различных типа данных!) =
④ Списки реализоZgudZddeZkkuKha^Zgb_kibkdw =— =это фактически
создание экземпляра класса. Т. =о., список имеет методы, которые работают
с ним. Метод extend ()=принимает один аргумент =— =список, и добаey_l
каждый его элемент к исходному списку. =

50

⑤ Метод insert () klZляет элемент в список. Первым аргументом яey_lky
индекс перh]hwe_f_glZ списке, который будет сдвинут ноuf
элементом со сh_cihabpbbWe_f_glukibkdZg_h[yaZgu[ulv
уникальными; например, теперь у нас есть два различных элемента со
значением 'Ω' : перucwe_f_gl a_list [0] и последний элемент a_list [6].

В Python конструкция a_list. insert (0, value ) дейстm_ldZdnmgdpby unshift () 
Perl . Она добаey_lwe_f_gl\gZqZehkibkdZZсе другие элементы
у_ebqbают сhcbg^_dkgZ_^bgbpmqlh[uhk\h[h^blvijhkljZgklо.
Давайте подробнее рассмотрим разницу между append () и extend ().
>>> a_list = ['a' , 'b' , 'c' ]
>>> a_list. extend (['d' , 'e' , 'f']) ①
>>> a_list
['a' , 'b' , 'c' , 'd' , 'e' , 'f']
>>> len (a_list ) ②
6
>>> a_list [-1]
'f'
>>> a_list. append (['g' , 'h' , 'i']) ③
>>> a_list
['a' , 'b' , 'c' , 'd' , 'e' , 'f', ['g' , 'h' , 'i']]
>>> len (a_list ) ④
7
>>> a_list [-1]
['g' , 'h' , 'i']
① Метод extend ()=принимает один аргумент, который k_]^Zyляется
списком, и добаey_ldZ`^ucwe_f_glwlh]hkibkdZd a_list K=
② Если uозьмёте список из трёх элементов и расширите его списком из
ещё трёх элементо\blh]_ihemqblkykibkhdbar_klbwe_f_glh\. =
③ С другой стороны, метод append ()=получает единст_ggucZj]mf_gl
который может быть любого типа. Здесь мы uauаем метод append (),
передаZy_fmkibkhdbaljzowe_f_glh\. =
④ Есл и uозьмёте список из шести элементов и добавите к нему список, 
итоге uihemqbl_kibkhdbak_fbwe_f_glh. Почему семь? Потому что
последний элемент (который мы только что добаbeb yляется списком.
Списки могут содержать любые типы данных, dexqw я другие списки.
Возможно, это то, что вам нужно, hafh`ghg_lGhwlhlhqlhы просили,
и это то, что вы получили. =

51

Поиск значений в списке
>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list.count('new') ①
2
>>> 'new' in a_list ②
True
>>> 'c' in a_list
False
>>> a_list.index('mpilgrim') ③
3
>>> a_list.index('new') ④
2
>>> a_list.index('c') ⑤
Traceback (innermost last):
File "", line 1, in ?
ValueError: list.index(x): x not in list
Переh^ сообщения оболочки :
Раскрутка стека (от g_rgbodнутренним):
Файл "<интерактиguch^!kljhdZihabpby ?
ValueError: list.index(x): x не в списке
① Как ugZерное ожидаете, метод count =возjZsZ_ldhebq_klо oh`^_gbc
указанного значения kibkhd. =
② Если всё, что Zfgm`gh =— =это узнать, присутстm_lebagZq_gb_\kibkd_
или нет, тогда оператор in =намного быстрее, чем метод count (). Оператор in =
k_]^Zозвращает True =или False ; он не сообщает, сколько именно в
списке данных значений. =
③ Если Zfg_h[oh^bfhlhqghagZlvgZdZdhff_kl_\kibkd_gZoh^blky
какое Jлибо значение, то используйте метод indeñ (). По умолчанию, он
просматриZ_l\_kvkibkhdghы можете указать вторым аргументом
индекс (отсчитыZ_fuchlgmey kdhlhjh]hg_h[oh^bfhgZqZlvihbkdb
даже третий аргумент =— =индекс, на котором необходимо останоblvihbkd. =
④ Метод indeñ ()=находит только первое oh`^_gb_agZq_gby списке. В
данном случае 'newD =klj_qZ_lky^\Z`^u списке:  a_list xOz=и a_list xQz, но
метод indeñ ()=_jgzllhevdhbg^_dki_jого oh`^_gby. =
⑤ Вопреки Zrbfh`b^Zgbyf_kebagZq_gb_g_gZc^_gh списке, то метод
indeñ ()=ha[m^blbkdexq_gb| Kxwa рJrob іn.с om z=
Постойте, что? Да, _jghf_lh^ index () ha[m`^Z_lbkdexq_gb__kebg_
может найти значение в списке. Вы на_jgh_aZf_lbebhlebqb_hl

52

большинства других языкоdhlhju_озjZsZxldZdhc -нибудь не_jguc
индекс (например, -1). Если на первый a]ey^wlhfh`_l немного раздражать,
то, я думаю, в будущем uijbf_l_wlhlih^oh^WlhhagZqZ_lqlhаша
программа будет аZjbcghaZершена в том месте, где hagbdeZijh[e_fZ
f_klhlh]hqlh[ulbohi_j_klZlvjZ[hlZlv\dZdhf -нибудь другом месте.
Запомните, -1 тоже яey_lkyih^oh^ysbfbg^_dkhf^eykibkdhy . Если бы метод
index () haращал -1, вас могли ожидать не_kzeu_ _q_jZihljZq_ggu_gZ
поиск ошибок в коде!
Удаление элементов из списка
Списки никогда не содержат разрывов.
Списки могут у_ebqbаться и сокращаться автоматически. Вы уже b^_ebdZd
они могут у_ebqbаться. Также сущестm_lg_kdhevdhjZaguokihkh[h\
удалить элементы из списка.
>>> a_list = ['a' , 'b' , 'new' , 'mpilgrim' , 'new' ]
>>> a_list [1]
'b'
>>> del a_list [1] ①
>>> a_list
['a' , 'new' , 'mpilgrim' , 'new' ]
>>> a_list [1] ②
'new'
① Можно использовать выражение del =для удаления определенного элемента
из списка. =
② Если после удаления элемента с индексом 1 опять попытаться прочитать
значение списка с индексом 1, это не uahет ошибки. Все элементы после
удаления смещают сhbbg^_dkuqlh[uaZihegblvijh[_eозникший
после удаления элемента. =
Не знаете индекс? Не беда — можно удалить элемент по значению.
>>> a_list.remove('new') ①
>>> a_list
['a', 'mpilgrim', 'new']
>>> a_list.remove('new') ②
>>> a_list
['a', 'mpilgrim']
>>> a_list.remove('new')
Traceback (most recent call last):
File "", line 1, in
ValueError: list.remove(x): x not in list
Переh^khh[s_gbch[hehqdb:

53

Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
ValueError: list.remove(x): x не kibkd_
① Можно удалить элемент из списка при помощи метода remove (). Метод
remove ()=dZq_kl\_iZjZf_ljZijbgbfZ_lagZq_gb_bm^Zey_li_jое
oh`^_gb_wlh]hagZq_gbybakibkdZDjhf_lh]hbg^_dku\k_owe_f_glh,
следующих за удалённым, будут сдbgmluqlh[uaZihegblvijh[_e
Списки никогда не содержат разрыво. =
② Можно uauать метод =remove ()=столько, сколько хотите, однако если
попытаться удалить значение, которого нет в списке, будет порождено
исключение. =
Удаление элементов из списка: дополнительный раунд
Другой интересный метод списков — pop (). Метод pop () — это еще один
способ удалить элементы из списка , но с одной особенностью.
>>> a_list = ['a', 'b', 'new', 'mpilgrim']
>>> a_list.pop() ①
'mpilgrim'
>>> a_list
['a', 'b', 'new']
>>> a_list.pop(1) ②
'b'
>>> a_list
['a', 'new']
>>> a_list.pop()
'new'
>>> a_list.pop()
'a'
>>> a_list.pop() ③
Traceback (most recent call last):
File "", line 1, in
IndexError: pop from empty list
Переh^khh[s_gbyh[hehqdb:
Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
IndexError: pop из пустого списка
① Если uaать pop ()=без аргументоhgm^Zeblihke_^gbcwe_f_glkibkdZb
_jg_lm^Ze_ggh_agZq_gb_. =

54

② С помощью метода pop () можно удалить любой элемент списка. Просто
uahите метод с индексом элемента. Этот элемент будет удалён, а все
элементы после него сместятся, чтобы «заполнить пробел». Метод
haращает удалённое из списка значение.
③ Метод pop () для пустого списка ha[m`^Z_lbkdexq_gb_.

Вызов метода pop () без аргументоwdиZe_gl_gызоmnmgdpbb pop () 
Perl . Он удаляет последний элемент из списка и haращает удалённое
значение. В языке программироZgby Perl есть также функция shift (),
которая удаляет перucwe_f_glb\haращает его значение. В Python это
экbалентно a_list. pop (0).
Списки в логическом контексте
Пустые списки — ложь, все остальные —
истина.
Вы также можете использоZlvkibkhd логическом контексте , например, 
операторе if:
>>> def is_it_true (anything ):
... if anything:
... print ("да , это истина ")
... else :
... print ("нет , это ложь ")
...
>>> is_it_true ([]) ①
нет , это ложь
>>> is_it_true (['a' ]) ②
да , это истина
>>> is_it_true ([False ]) ③
да , это истина
① В логическом контексте пустой список =— =ложь. =
② Любой список, состоящий хотя бы из одного элемента, =— =истина. =
③ Любой список, состоящий хотя бы из одного элемента, =— =истина. Значения
элементоg_ажны. =

55

Кортежи
Кортеж — это неизменяемый список. Кортеж не может быть изменён никаким
способом после его создания.
>>> a_tuple = ("a" , "b" , "mpilgrim" , "z" , "example" ) ①
>>> a_tuple
('a' , 'b' , 'mpilgrim' , 'z' , 'example' )
>>> a_tuple [0] ②
'a'
>>> a_tuple [-1] ③
'example'
>>> a_tuple [1:3] ④
('b' , 'mpilgrim' )
① Кортеж определяется так же, как список, за исключением того, что набор
элементоaZdexqZ_lky круглые скобки, а не в квадратные. =
② Элементы кортежа заданы hij_^_ezgghfihjy^d_dZdb\kibkd_
Элементы кортежа индексируются с нуля, как и элементы списка, таким
образом перucwe_f_glg_imklh]hdhjl_`w =— =это k_]^Z a_tuple xMzK=
③ Отрицательные значения индекса отсчитываются от конца кортежа, как и 
списке. =
④ Создание среза кортежа («slicing») аналогично созданию среза списка.
Когда создаётся срез списка, получается новый список; когда создаётся
срез кортежа, получается новый кортеж. =
Осноgh_hlebqb_f_`^mdhjl_`ZfbbkibkdZfbkhklhbl\lhfqlhdhjl_`bg_
могут быть изменены. Гоhjyl_ogbq_kdbfyaudhfdhjl_} — неизменяемый
объект . На практике это означает, что у них нет методов, которые бы
позhebebbobaf_gblvMkibkdh\_klvlZdb_ методы, как append (), extend (),
insert (), remove (), и pop (). У кортежей ни одного из этих методов нет. Можно
aylvkj_ahldhjl_`Z lZddZdijbwlhfkha^Zklkygh\ucdhjl_` fh`gh
про_jblvkh^_j`blebdhjl_`we_f_glkdhgdj_lgufagZq_gb_f lZddZdwlh
де йстb_g_baf_gbldhjl_` bkh[kl\_ggh , всё .
# продолжение предыдущего примера
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple.append("new") ①
Traceback (innermost last):
File "", line 1, in ?
AttributeError: 'tuple' object has no attribute 'append'
>>> a_tuple.remove("z") ②
Traceback (innermost last):

56

File "", line 1, in ?
AttributeError: 'tuple' object has no attribute 'remove'
>>> a_tuple.index("example") ③
4
>>> "z" in a_tuple ④
True
Переh^khh[s_gbch[hehqdb:
Раскрутка стека (от g_rgbodнутренним):
Файл "<интерактиguch^!kljhdZihabpby ?
AttributeError: у объекта 'tuple' нет атрибута '<атрибут>'
① Вы не можете добаblvwe_f_gluddhjl_`mDhjl_`bg_bf_xlf_lh^h\
append ()=или extend ()K=
② Вы не можете удалять элементы из кортежа. Кортежи не имеют методов
remove ()=или pop ()K=
③ Вы можете искать элементы в кортежи, поскольку это не изменяет кортеж. =
④ Вы также можете использоZlvhi_jZlhj in , чтобы про_jblvkms_klует
ли элемент в кортеже. =
Так где же могут пригодиться кортежи?
 Кортежи быстрее, чем списки. Если uhij_^_ey_l_g_baf_gy_fuc
набор значений и kzqlh\ukh[bjZ_l_kvkgbf^_eZlv — итерироZlv
по нему, используйте кортеж f_klhkibkdZ.
 Кортежи делают код безопаснее в том случае, если у Zk_klv
«защищенные от записи» данные, которые не должны изменяться.
Использование кортежей f_klhkibkdh\ba[Zит Zkhlg_h[oh^bfhklb
использоZlv оператор assert , дающий понять, что данные неизменяемы,
и что нужно приложить особые усилия (и особую функцию), чтобы это
обойти.
 Некоторые кортежи могут использоваться в качест_dexq_ckeh\Zjy
(конкретно, кортежи, содержащие неизменяемые значения, наприм ер,
строки, числа и другие кортежи). Списки никогда не могут использоZlvky
dZq_klе ключей словаря, потому что списки — изменяемые объекты.

Кортежи могут быть преобразоZgu списки и наоборот. Встроенная
функция tuple ()=принимает список и haращает кортеж из всех его
элементоnmgdpby list ()=принимает кортеж и возjZsZ_lkibkhdIhkmlb
дела, tuple ()=заморажиZ_lkibkhdZ list ()=разморажиZ_ldhjl_`. =

57

Кортежи в логическом контексте
Вы можете использоZlvdhjl_`b\eh]bq_kdhfdhgl_dkl_gZijbf_j\
операторе if.
>>> def is_it_true (anything ):
... if anything:
... print ("да , это истина ")
... else :
... print ("нет , это ложь ")
...
>>> is_it_true (()) ①
нет , это ложь
>>> is_it_true (('a' , 'b' )) ②
да , это истина
>>> is_it_true ((False ,)) ③
да , это истина
>>> type ((False )) ④

>>> type ((False ,))

① В логическом контексте пустой кортеж яey_lkyeh`vx. =
② Любой кортеж состоящий по крайней мере из одного элемента — =истина. =
③ Любой кортеж состоящий по крайней мере из одного элемента — =истина.
Значения элементоg_ажны. Но что делает здесь эта запятая? =
④ Чтобы создать кортеж из одного элемента, необходимо после него
постаblvaZiylmx;_aaZiylhc3\WKRQij_^iheZ]Z_lqlh\uijhklh
добаbeb_s_h^gmiZjmkdh[hdqlhg_^_eZ_lgbq_]hiehoh]hghbg_
создает кортеж. =
Присваивание нескольких значений за раз
Вот крутой программерский прием: 3\WKRQfh`ghbkihevahать кор тежи,
чтобы присZbать значение нескольким переменным сразу.
>>> v = ('a' , 2, True )
>>> (x, y, z) = v ①
>>> x
'a'
>>> y
2
>>> z
True

58

① v — это кортеж из трех элементоZ (x, y, z) — кортеж из трёх переменных.
Присh_gb_h^gh]h^jm]hfmijbодит к присh_gbxdZ`^h]hagZq_gbyba v
каждой переменной mdZaZgghfihjy^d_.
Это не единст_gguckihkh[bkihevahания. Предположим, что uohlbl_
присhblvbf_gZ^bZiZahgmagZq_gbc<ufh`_l_b спользоZlv\kljh_ggmx
функцию range () для быстрого присвоения сразу нескольких
последоZl_evguoagZq_gbc.
>>> (MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY ,
SUNDAY ) = range (7) ①
>>> MONDAY ②
0
>>> TUESDAY
1
>>> SUNDAY
6
① Встроенная функция range ()=создаёт последоZl_evghklvp_euoqbk_e
(Строго гоhjynmgdpby range ()=haращает итератор, а не список или
кортеж, но umagZ_l_jZagbpmqmlviha`_ MONDAY I=TUESDAv I=
WEDNESDAv I=THURSDAY I=FRIDAY I=SATURDAY I=и=SUNDAY =— =
определяемые =переменные . (Этот пример заимстhан из модуля
, небольшого забаgh]hfh^meydhlhjuchlh[jZ`Z_ldZe_g^Zjb calendar
примерно как программа cal =из UNIX . В этом модуле определяются
константы целого типа для дней недели.) =
② Теперь каждой переменной присh_ghdhgdj_lgh_agZq_gb_ MONDAY =— =
это 0, TUESDAv =— =1, и так далее. =
Вы также можете использоZlvijbkоение значений нескольким переменным
сразу, чтобы создаZlvnmgdpbbозjZsZxsb_g_kdhevdhagZq_gbc^ey
этого достаточно просто _jgmlvdhjl_`kh^_j`ZsbcwlbagZq_gby<lhf
месте программы, где была uaана функция, ha\jZsZ_fh_agZq_gb_fh`gh
использоZlvdZddhjl_`p_ebdhfbebijbkоить значения нескольких
отдельных переменных. Этот приём используется во многих стандартных
библиотеках Python, dexqZybfh^mev os , о котором вы узнаете в следующей
гла| .
Мн ожества
Множестh — это «мешок», содержащий неупорядоченные уникальные
значения. Одно множестhfh`_lkh^_j`ZlvagZq_gbyex[uolbih. Если у
Zk_klv^а множестZы можете со_jrZlvgZ^gbfbex[u_klZg^Zjlgu_
операции, например, объединение, пересечение и разность.

59

Создание множества
Начнём с самого начала. Создать множество очень легко .
>>> a_set = {1} ①
>>> a_set
{1}
>>> type (a_set ) ②

>>> a_set = {1, 2} ③
>>> a_set
{1, 2}
① Чтобы создать множество с одним значением, поместите его =в фигурные
скобки ( ).= {}
② МножестZообще Jто, реализуются как классы, но пока не беспокойтесь
об этом. =
③ Чтобы создать множество с несколькими значениями, отделите их друг от
друга запятыми и поместите внутрь фигурных скобок. =
Также ufh`_l_kha^Zlvfgh`_kl\hba списка .
>>> a_list = ['a' , 'b' , 'mpilgrim' , True , False , 42 ]
>>> a_set = set (a_list ) ①
>>> a_set ②
{'a' , False , 'b' , True , 'mpilgrim' , 42 }
>>> a_list ③
['a' , 'b' , 'mpilgrim' , True , False , 42 ]
① Чтобы создать множество из списка, hkihevamcl_kvnmgdpb_c set ().
(Педанты, которые знают как реализоZgufgh`_klа, отметят, что на
самом деле это создание экземпляра класса, а не uah функции. Я
обещаю , umagZ_l_\qzfjZagbpZ^Ze__ этой книге. Сейчас просто
знайте, что set ()=ведет себя как функция и ha\jZsZ_lfgh`_klо.) =
② Как я упоминал ранее, множестhfh`_lkh^_j`ZlvagZq_gbyex[uolbih.
И, как я упоминал ранее, множестZ неупорядочены . Это множестhg_
помнит перhgZqZevgucihjy^hdkibkdZbadhlhjh]hhgh[uehkha^Zgh. =
Если u^h[Zляете элементы fgh`_kl\hhghg_aZihfbgZ_l\dZdhf
порядке они добаeyebkv. =
③ Исходный список не изменился. =
У Zk_szg_lagZq_gbc"G_lijh[e_fFh`ghkha^Zlvimklh_fgh`_kl\h.
>>> a_set = set () ①
>>> a_set ②

60

set ()
>>> type (a_set ) ③

>>> len (a_set ) ④
0
>>> not_sure = {} ⑤
>>> type (not_sure )

① Чтобы создать пустое множестh\uahите set ()=без аргументо. =
② Напечатанное предстаe_gb_imklh]hfgh`_kl\Zыглядит немного
странно. Вы, на_jgh_h`b^Zebmидеть ? Это означало бы пустой {}
словарь, а не пустое множестhHkeh\Zjyoы узнаете далее в этой
гла_. =
③ Несмотря на странное печатное предстаe_gb_wlh дейстbl_evgh
множестh. =
④ ...и это множестhg_kh^_j`blgbh^gh]h элемента. =
⑤ В силу исторических причуд, пришедших из Python =2, нельзя создать
пустое множестhkihfhsvx^ух фигурных скобок. На самом деле, они
создают пустой слоZjvZg_fgh`_klо. =
Изменение множества
Есть дZkihkh[Z^h[Zить элементы kms_klующее множество: метод add ()
и метод update ().
>>> a_set = {1, 2}
>>> a_set. add (4) ①
>>> a_set
{1, 2, 4}
>>> len (a_set ) ②
3
>>> a_set. add (1) ③
>>> a_set
{1, 2, 4}
>>> len (a_set ) ④
3
① Метод add ()=принимает один аргумент, который может быть любого типа, и
добаey_l^Zggh_agZq_gb_\fgh`_klо. =
② Теперь множество содержит 3 элемента. =

61

③ Множестw — мешки уникальных значений . Если попытаться добаblv
значение, которое уже присутстm_ly множест_gbq_]hg_ijhbahc^_l
Это не при_^_l\озникно_gbxhrb[dbijhklhgme_ое дейстb_.
④ Это множестh все ещё состоит из 3 элементов.
>>> a_set = {1, 2, 3}
>>> a_set
{1, 2, 3}
>>> a_set. update ({2, 4, 6}) ①
>>> a_set ②
{1, 2, 3, 4, 6}
>>> a_set. update ({3, 6, 9}, {1, 2, 3, 5, 8, 13 }) ③
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 13 }
>>> a_set. update ([10 , 20 , 30 ]) ④
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 10 , 13 , 20 , 30 }
① Метод update ()=принимает один аргумент =— =множество, и добаey_l\k_
его элементы к исходному множеству. Так, как если бы uызыZebf_lh^
add ()=и по очереди передаZeb_fmсе элементы множестZ. =
② Поlhjyxsb_kyagZq_gbyb]ghjbjmxlkyihkdhevdm множестhg_fh`_l
содержать дубликаты. =
③ Вообще Jто, ufh`_l_ызZlvf_lh^ update ()=с любым количестhf
параметроDh]^ZhgызыZ_lkyk^умя множестZfbf_lh^ update ()=
добаey_l\k_we_f_gluh[hbofgh`_kl\\bkoh^gh_fgh`_klо (пропуская
поlhjyxsb_ky . =
④ Метод update ()=может принимать объекты различных типоключая
списки. Когда ему передается список, он добаey_lсе его элементы в
исходное множестh. =
Удаление элементов множества
Сущестmxlljbkihkh[Zm^Ze_gbyhl^_evguoagZq_gbcbafgh`_klа. Первые
дw , discard () и remove (), немного различаются .
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set
{1, 3, 36, 6, 10, 45, 15, 21, 28}
>>> a_set.discard(10) ①
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.discard(10) ②

62

>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.remove(21) ③
>>> a_set
{1, 3, 36, 6, 45, 15, 28}
>>> a_set.remove(21) ④
Traceback (most recent call last):
File "", line 1, in
KeyError: 21
Переh^khh[s_gbyh[hehqdb:
Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
KeyError: 21
① Метод discard ()=принимает в качестве аргумента одиночное значение и
удаляет это значение из множестZ. =
② Если uызZebf_lh^ discard ()=переда_fmagZq_gb_dhlhjh]hg_l\
множест_gbq_]hg_ijhbahc^_lijhklhgme_\h_^_cklие. =
③ Метод remove ()=также =принимает dZq_klе аргумента одиночное
значение, и также удаляет его из множества. =
④ Вот в чём отличие: если значения нет в множест_f_lh^ remove ()=породит
исключение KeyError K=
Подобно спискам, множестZbf_xlf_lh^ pop ().
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set.pop() ①
1
>>> a_set.pop()
3
>>> a_set.pop()
36
>>> a_set
{6, 10, 45, 15, 21, 28}
>>> a_set.clear() ②
>>> a_set
set()
>>> a_set.pop() ③
Traceback (most recent call last):
File "", line 1, in
KeyError: 'pop from an empty set'
Переh^khh[s_gbyh[hehqdb:

63

Раскрутка стека (список последних uahо :
Файл "", строка 1, <модуль>
Ke(UURU
SRSbaimklh]hfgh`_klа'
① Метод pop ()=удаляет один элемент из множестZbозjZsZ_l_]h
значение. Однако, поскольку множестZg_mihjy^hq_guwlhg_
«последний» элемент fgh`_klе, поэтому неhafh`gh
проконтролировать какое значение было удалено. Удаляется
произhevgucwe_f_gl. =
② Метод clear ()=удаляет все элементы множестZhklZ\eyyас с пустым
множестhfWlhwdиZe_glghaZibkb a_set Z=set (), которая создаст ноh_
пустое множестhbi_j_aZibr_lij_^u^ms__agZq_gb_i_j_f_gghc a_set K=
③ Попытка изe_q_gby SRS we_f_glZbaimklh]hfgh`_klа породит
исключение KeyError K=
Основные операции с множествами
Тип set 3\WKRQih^^_j`bает несколько основных операций над
множестZfb.
>>> a_set = {2, 4, 5, 9, 12 , 21 , 30 , 51 , 76 , 127 , 195 }
>>> 30 in a_set ①
True
>>> 31 in a_set
False
>>> b_set = {1, 2, 3, 5, 6, 8, 9, 12 , 15 , 17 , 18 , 21 }
>>> a_set. union (b_set ) ②
{1, 2, 195 , 4, 5, 6, 8, 12 , 76 , 15 , 17 , 18 , 3, 21 , 30 , 51 , 9, 127 }
>>> a_set. intersection (b_set ) ③
{9, 2, 12 , 5, 21 }
>>> a_set. difference (b_set ) ④
{195 , 4, 76 , 51 , 30 , 127 }
>>> a_set. symmetric_difference (b_set ) ⑤
{1, 3, 4, 6, 8, 76 , 15 , 17 , 18 , 195 , 127 , 30 , 51 }
① Чтобы про_jblvijbgZ^e_`blebagZq_gb_fgh`_kl\mbkihevamcl_
оператор in . Он работает так же, как и для списков. =
② Метод union ()=(объединение) ha\jZsZ_lghое множестhkh^_j`Zs__
k_we_f_gludZ`^h]hbafgh`_kl. =
③ Метод intersection ()=(пересечение) haращает ноh_fgh`_klо,
содержащее k_we_f_gludhlhju__klvb\i_jом множест_bо
lhjhf. =

64

④ Метод difference () (разнос ть) haращает ноh_fgh`_klо, содержащее
k_we_f_gludhlhju__klv\fgh`_klе a_set , но которых нет в
множест_ b_set .
⑤ Метод symmetric_difference () (симметрическая разность) haращает новое
множестhdhlhjh_kh^_j`bllhevdhmgbdZevgu_we_f_gluh[hbo
множест.
Три из этих методоkbff_ljbqgu.
# продолжение предыдущего примера
>>> b_set. symmetric_difference (a_set ) ①
{3, 1, 195 , 4, 6, 8, 76 , 15 , 17 , 18 , 51 , 30 , 127 }
>>> b_set. symmetric_difference (a_set ) == a_set. symmetric_difference (b_set ) ②
True
>>> b_set. union (a_set ) == a_set. union (b_set ) ③
True
>>> b_set. intersection (a_set ) == a_set. intersection (b_set ) ④
True
>>> b_set. difference (a_set ) == a_set. difference (b_set ) ⑤
False
① Симметрическая разность множеств a_set =и b_set =u]ey^bl не так, как
симметрическая разность множеств b_set =и a_set , но вспомните, множестZ
неупорядочены. Любые дZfgh`_klа, все (без исключения) значения
которых одинаковы, считаются равными. =
② Именно это здесь и произошло. Глядя на печатное предстаe_gb_wl их
множестkha^Zggh_h[hehqdhc3\WKRQg_h[fZguайтесь. Значения
элементоwlbofgh`_kl\h^bgZdh\uihwlhfmhgbjZны. =
③ Объединение дmofgh`_kl\lZd`_kbff_ljbqgh. =
④ Пересечение дmofgh`_kl\lZd`_kbff_ljbqgh. =
⑤ Разность дmofgh`_kl\g_kbff_ljbqg а. По смыслу, данная операция
аналогична uqblZgbxh^gh]hqbkeZba^jm]h]hIhjy^hdhi_jZg^h\bf__l
значение. =
Наконец, есть ещё несколько hijhkh по множестZfdhlhju_ы можете
задать.
>>> a_set = {1, 2, 3}
>>> b_set = {1, 2, 3, 4}
>>> a_set. issubset (b_set ) ①
True
>>> b_set. issuperset (a_set ) ②
True

65

>>> a_set. add (5) ③
>>> a_set. issubset (b_set )
False
>>> b_set. issuperset (a_set )
False
① Множестh a_set =яey_lkyih^fgh`_klом b_set =— =k_we_f_glu a_set =
также яeyxlkywe_f_glZfb b_set K=
② И наоборот, b_set =яey_lkygZ^fgh`_klом a_set , потому что k_
элементы a_set =также яeyxlkywe_f_glZfb b_set K=
③ Поскольку u^h[Zили элемент в a_set , но не добаbeb b_set , обе
про_jdbернут значение False K=
Множества в логическом контексте
Вы можете использоZlvfgh`_klа  логическом контексте , например, 
операторе if.
>>> def is_it_true (anything ):
... if anything:
... print ("да , это истина ")
... else :
... print ("нет , это ложь ")
...
>>> is_it_true (set ()) ①
нет , это ложь
>>> is_it_true ({'a' }) ②
да , это истина
>>> is_it_true ({False }) ③
да , это истина
① В логическом контексте пустое множестh =— =ложь. =
② Любое множестhkh^_j`Zs__ohly[uh^bgwe_f_gl =— =истина. =
③ Любое множестhkh^_j`Zs__ohly[uh^bgwe_f_gl =— =истина. Значения
элементоg_ажны. =
Словари
Словарь — это неупорядоченное множество пар ключ — значение. Когда u
добаey_l_dexq словарь, ulZd`_^he`gu^h[Z\blvbagZq_gb_^eywlh]h
ключа. (Значение k_]^Zfh`ghbaf_gblviha`_ Keh\Zjb Python
оптимизироZgu^eyihemq_gbyagZq_gbyihbaестному ключу, но не для
других целей.

66


Словарь 3\WKRQZgZeh]bq_gowrm\ Perl 5. В Perl 5 переменные,
хранящие хэши, всегда начинаются с симheZ % . В Python переменные
могут быть назZgudZdm]h^ghyaudkZfhlke_`bает типы данных.
Создание словаря
Создать словарь очень просто. Синтаксис похож на синтаксис создания
множестghместо элементоbkihevamxlkyiZjudexq -значение. Если у
Zk_klvkeh\Zjvы можете просматриZlvagZq_gbyihbodexqm.
>>> a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'} ①
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['server'] ②
'db.diveintopython3.org'
>>> a_dict['database'] ③
'mysql'
>>> a_d ict['db.diveintopython3.org'] ④
Traceback (most recent call last):
File "", line 1, in
KeyError: 'db.diveintopython3.org'
Переh^khh[s_gbyh[hehqdb:
Раскрутка стека (список последних uahо :
Файл " ", строка 1, <модуль>
KeyError: 'db.diveintopython3.org'
① Сначала вы создаёте ноuckeh\Zjvk^умя элементами и присZbаете
его переменной a_dict . Каждый элемент яey_lkyiZjhcdexq — значение, а
_kvgZ[hjwe_f_glh\aZdexqzg\nb]mjgu_kdh[db. =
② 'server' яey_lkydexqhfbhgkязан со значением, обращение к которому
с помощью a_dict x'server' z=даст нам 'db.diveintopython3.org' K=
③ 'database' яey_lkydexqhfbhgkязан со значением, обращение к
которому с помощью a_dict x'database' z=даст нам 'mysq l'K=
④ Можно получить значение по ключу, но нельзя получить ключи по
значению. Так a_dict x'server' z=— =это 'db.diveintopython3.org' , но
a_dict x'db.diveintopython3.org' z=породит исключение, потому что
'db.diveintopython3.org' =не яey_lkydexqhf. =
Изменение словаря
Словари не имеют какого -либо предопределенного ограничения размера.
Когда угодно можно добаeylvgh\u_iZjudexq — значение keh\Zjvbeb

67

изменять значение, соот_lklующее сущестmxs_fmdexqmIjh^he`bf
предыдущий пример:
>>> a_dict
{'server' : 'db.diveintopython3.org' , 'database' : 'mysql' }
>>> a_dict ['database' ] = 'blog' ①
>>> a_dict
{'server' : 'db.diveintopython3.org' , 'database' : 'blog' }
>>> a_dict ['user' ] = 'mark' ②
>>> a_dict ③
{'server' : 'db.diveintopython3.org' , 'user' : 'mark' , 'database' : 'blog' }
>>> a_dict ['user' ] = 'dora' ④
>>> a_dict
{'server' : 'db.diveintopython3.org' , 'user' : 'dora' , 'database' : 'blog' }
>>> a_dict ['User' ] = 'mark' ⑤
>>> a_dict
{'User' : 'mark' , 'server' : 'db.diveintopython3.org' , 'user' : 'dora' , 'database' : 'blog' }
① Ваш словарь не может содержать одинаковые ключи. Присh_gb_agZq_gby
сущестmxs_fmdexqmmgbqlh`blklZjh_agZq_gb_. =
② Можно добаeylvghые пары ключ — значение когда угодно. Данный
синтаксис идентичен синтаксису модифицироZgbykms_klующих
значений. =
③ Кажется, что ноucwe_f_glkeh\Zjy dexq 'user' , значение 'mark' ) попал 
середину. На самом деле, это всего лишь соiZ^_gb_qlhwe_f_glu
кажутся расположенными по порядку i_jом примере; такое же
соiZ^_gb_ что теперь они u]ey^yljZkiheh`_ggufbg_ihihjy^dm. =
④ Присh_gb_agZq_gbykms_klующему ключу просто заменяет старое
значение ноuf. =
⑤ Изменится ли значение ключа 'user' =обратно на "mark" ? Нет! Посмотрите на
него gbfZl_evg_| =— =ключ "User" =написан с за главной буквы. Ключи
словаря регистрозаbkbfuihwlhfmwlhыражение создаст ноmxiZjm
ключ — значение, а не перезапишет сущестmxsmx<ZfdZ`_lkyqlhdexqb
похожи, а с точки зрения Python они абсолютно разные. =
Словари со смешанными значениями
Словари могут состоять не только из строк. Значения словарей могут быть
любого типа, dexqZyp_eu_eh]bq_kdb_ijhbaольные объекты, или даже
другие словари. И значения h^ghfkeh\Zj_g_h[yaZgu[ulvh^gh]hblh]h
же типа; можно смешивать и сочетать их, как Zfg_h[oh^bfhDexqbkehаря
более ограниченны, но они могут быть строками, целыми числами и

68

некоторыми другими типами. Ключи разных типов тоже можно смешивать и
сочетать в одном словаре.
На самом деле um`_идели словарь с не строкоufbdexqZfbb
значениями в Zr_ci_jой программе на Python.
SUFFIXES = {1000 : ['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ],
1024 : ['KiB' , 'MiB' , 'GiB' , 'TiB' , 'PiB' , 'EiB' , 'ZiB' , 'YiB' ]}
Давайте ulZsbfwlmi_j_f_ggmxbagZr_cijh]jZffubihjZ[hlZ_fk ней в
интерактиghch[hehqd_3\WKRQ.
>>> SUFFIXES = {1000 : ['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ],
... 1024 : ['KiB' , 'MiB' , 'GiB' , 'TiB' , 'PiB' , 'EiB' , 'ZiB' , 'YiB' ]}
>>> len (SUFFIXES ) ①
2
>>> 1000 in SUFFIXES ②
True
>>> SUFFIXES [1000 ] ③
['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ]
>>> SUFFIXES [1024 ] ④
['KiB' , 'MiB' , 'GiB' , 'TiB' , 'PiB' , 'EiB' , 'ZiB' , 'YiB' ]
>>> SUFFIXES [1000 ][3] ⑤
'TB'
① Так же, как для списков и множеств , функция len ()=ha\jZsZ_ldhebq_kl\h
элементоkeh\Zjy. =
② И так же, как со списками и множестZfbfh`ghbkihevahать оператор in ,
чтобы про_jblvhij_^_ezgebdhgdj_lgucdexq\kehаре. =
③ 1000 яey_lky ключом в словаре SUFFIXES ; его значение =— =список из
hkvfb элементов (hkvfbkljhd_keb[ulvlhqguf . =
④ Аналогично, 1024 =— =ключ словаря SUFFIXES ; и его значение также
яey_lkykibkdhfbaосьми элементо. =
⑤ Так как SUFFIXES x1000 z=яey_lkykibkdhflhfh`ghh[jZlblvkyd
отдельным элементам списка по их порядкоu м номерам, которые
индексируются с нуля. =
Словари в логическом контексте
Пустые словари — ложь, все остальные —
истина.

69

Вы можете использоZlvkeh\Zjb\ логическом контексте , например в
операторе if.
>>> def is_it_true (anything ):
... if anything:
... print ("да , это истина ")
... else :
... print ("нет, это ложь" )
...
>>> is_it_true ({}) ①
нет , это ложь
>>> is_it_true ({'a' : 1}) ②
да , это истина
① В логическом контексте пустой словарь ложен. =
② Любой словарь с хотя бы одной парой ключ — значение истинен. =
Константа None
None — это специальная константа 3\WKRQHgZh[hagZqZ_limklh_agZq_gb_
None — это не то же самое, что False . None также и не 0. None даже не пустая
строка. Если сравниZlv None с другими типами данных, то результатом
k_]^Z[m^_l False .
None — это просто пустое значение. None имеет свой собст_gguclbi
(NoneType ). Вы можете присhblv None любой переменной, но ug_fh`_l_
создать других объектоlbiZ NoneType . Все переменные , значение которых
None раgu друг другу .
>>> type (None )

>>> None == False
False
>>> None == 0
False
>>> None == ''
False
>>> None == None
True
>>> x = None
>>> x == None
True
>>> y = None
>>> x == y
True

70

None в логическом контексте
В логическом контексте None всегда яey_lkyeh`vxZ not None — истиной.
>>> def is_it_true (anything ):
... if anything:
... print ("да , это истина ")
... else :
... print ("нет , это ложь ")
...
>>> is_it_true (None )
нет , это ложь
>>> is_it_true (not None )
да , это истина
Материалы для дальнейшего чтения
 Логические операции
 Численные типы
 Типы -последовательности
 Типы -множества
 Типы -отображения
 Модуль fractions
 Модуль ma th
 PEP 237: Унификация длинных целых и целых
 PEP 238: Изменение оператора деления

71

Генераторы

Нам приходится сильнее напрягать сh_\hh[jZ`_gb_g_^eylh]hqlh[udZd\
художест_gghcebl_jZlmj_ij_^klZить себе то, чего нет на самом деле, а
для того, чтобы пости чь то, что дейстbl_evghijhbkoh^bl. =
Ричард Фейнман
Погружение
В каждом языке программироZgby_klvh^gZlZdZyhkh[_gghklvkeh`gh
устроенная, но специально упрощённая штука. Если ujZgvr_ibkZebgZ
другом языке, можете и не обратить на это внимания, поскольку ZrklZjuc
язык не так сильно упрощал эту штуку (потому что он был занят тем, что
сильно упрощал какую -нибудь другую штуку). В этой главе ubamqbl_
генераторы спискоkehарей и множеств — три взаимосyaZggu_dhgp_ipbb
сконцентрироZggu_окру г одной очень мощной технологии. Но сначала я
хочу немного отклониться от нашего по_klоZgbyqlh[ujZkkdZaZlv\Zfh
дmofh^meyodhlhju_ihfh]mlам передb]Zlvkyihашей локальной
файловой системе.
Работа с файлами и каталогами
Python 3 постаey_lky с модулем os , что означает «операционная система».
Модуль os содержит множестhnmgdpbc^eyihemq_gbybgnhjfZpbbh
локальных каталогах, файлах, процессах и переменных окружения (а в
некоторых случа ях, и для манипулироZgbybfb 3\WKRQij_^eZ]Z_lhq_gv
хороший унифицироZgguc программный интерфейс для k_o
поддержиZ_fuohi_jZpbhgguokbkl_flZdqlhаши программы можно
запускать на любом компьютере с минимальным количестhfieZlnhjfh -
заbkbfh]hdh{ а.
Текущий рабочий каталог
Когда Zr_agZdhfklо с Python только начинается, ufgh]hремени
проh^bl_\bgl_jZdlb\ghch[hehqd_3\WKRQGZijhly`_gbb\k_cwlhcdgb]b
u[m^_l_идеть примеры, u]ey^ysb_ke_^mxsbfh[jZahf:

72

1. ИмпортироZgb_dZdh]h -либо модуля из папки примеров
2. Вызов функции из этого модуля
3. Объяснение результата
Всегда есть текущий рабочий каталог.
Если ugbq_]hg_agZ_l_hl_dms_fjZ[hq_fdZlZeh]_lhоз можно, шаг 1
окажется неудачным и будет порождено исключение типа ImportError . Почему?
Потому что Python будет искать указанный модуль imlbihbkdZhi_jZlhjZ
import , но не найдёт его, потому что каталог не содержится imlyo examples
ihbkdZQlh[ubkijZblvwlhufh`_l_k^_eZlvh^ghba^mo
 либо добаblviZidm imlvihbkdZhi_jZlhjZ import ; examples
 либо сделать текущим рабочим каталогом папку . examples
Текущий рабочий каталог яey_lkyg_yным параметром, который Python
постоянно хранит iZfylbL_dmsbcjZ[hqbcdZlZeh]_klv\k_]^Zdh]^Zы
работаете bgl_jZdlb\ghch[hehqd_3\WKRQaZimkdZ_l_kой сценарии из
командной строки или CGI -сценарий где -то на _x -сер_j| .
Модуль os содержит д_nmgdpbb^eyjZ[hlukl_dmsbfjZ[hqbfdZlZeh]hf.
>>> import os [1]
>>> print (os .getcwd ())
C: \Python31
[2]
>>> os .chdir ('/Users/pilgrim/diveintopython3/examples' ) [3]
>>> print (os .getcwd ())
C: \Users \pilgrim \diveintopython3 \examples
[4]
1. ↑ Модуль os постаey_lkyместе с Python; ufh`_l_bfihjlbjhать
его когда угодно и где угодно.
2. ↑ Используйте функцию os .getcwd () для получения значения текущего
рабочего каталога. Когда вы работаете в графической оболочке Python,
текущим рабочим каталогом яey_lkydZlZeh]badhlhjh]hhgZ[ueZ
запущена. В Windows это заbkblhllh]hdm^Zы устаноbeb3\WKRQ
каталог по умолчанию . Если оболочка Python запущена из c: \Python31
dhfZg^ghc kljhdbl_dmsbfjZ[hqbfdZlZeh]hfkqblZ_lkylhl\dhlhjhf
ugZoh^bebkvdh]^ZaZimkdZeb_z
3. ↑ Используйте функцию os .chdir () чтобы сменить текущий рабочий
каталог.
4. ↑ Когда я uauал функцию os .chdir (), я использоZeimlv стиле Linux
(косая черта , нет буквы диска) даже если на самом деле работал в

73

Windows . Это одно из тех мест, где Python пытается стирать различия
между операционными системами.
Работа с именами файлов и каталогов
Раз зашла речь о каталогах, я хочу обратить Zr_\gbfZgb_gZfh^mev
os .path . Он содержит функции для работы с именами файлов и каталого.
>>> import os
>>> print (os .path .join ('/Users/pilgrim/diveintopython3/examples/' , 'humansize.py' ))
/Users/pilgrim/diveintopython3/examples/humansize. py
[1]
>>> print (os .path .join ('/Users/pilgrim/diveintopython3/examples' , 'humansize.py' ))
/Users/pilgrim/diveintopython3/examples \humansize. py
[2]
>>> print (os .path .expanduser ('~' ))
c: \Users \pilgrim
[3]
>>> print (os .path .join (os .path .expanduser ('~' ), 'diveintopython3' , 'examples' ,
'humansize.py' ))
c: \Users \pilgrim \diveintopython3 \examples \humansize. py
[4]
1. ↑ Функция os .path .join () состаey_limlvddZlZeh]mbah^gh]hbeb
нескольких частичных путей. В данном случае она просто соединяет
строки.
2. ↑ Это уже менее триbZevguckemqZcNmgdpby join добаbl
дополни тельную косую черту (slash) к имени папки перед тем как
дописать имя файла. В данном случае Python добаey_lh[jZlgmxdhkmx
черту (backslash) f_klhh[udghенной, потому что я запустил этот
пример  Windows . Если u_^zl_^ZggmxdhfZg^m Linux или
Mac OS X, umидите простую косую черту. Python может обратиться к
файлу незаbkbfhhllh]hdZdhcjZa^_ebl_evbkihevam_lky\imlbd
файлу.
3. ↑ Функция os .path .expanduser () раскрыв ает путь, в котором используется
симhe для обозначения домашнего каталога текущего пользоZl_ey ~
NmgdpbyjZ[hlZ_lgZex[hcieZlnhjf_]^_mihevahZl_ey_klv
^hfZrgbcdZlZeh]dexqZy/LQX[0DF OS X и Windows. Функция
haращает путь без косой черты в конце, но для функции os .path .join ()
это не имеет значения.
4. ↑ Комбинируя эти д_nmgdpbb\ufh`_l_e_]dhkljhblvnZceh\u_imlb
для папок и файло домашнем каталоге пользоZl_eyNmgdpby
os .path .join () принимает любое количестhZj]mf_glh\Yihemqbe
огромное удовольстb_dh]^Zh[gZjm`bewlhlZddZd\^jm]boyaudZoijb
разработке инструментальных средстfg_ijboh^behkvihklhyggh

74

писать глупую маленькую функцию addSlashIfNecessary (). В языке
программи роZgby3\WKRQmfgu_ex^bm`_ihaZ[hlbebkvh[wlhf.
Модуль os .path также содержит функции для разбиения файловых путей, имён
папок и файлов на их состаgu_qZklb.
>>> pathname = '/Users/pilgrim/diveintopython3/examples/humansize.py'
>>> os .path .split (pathname )
('/Users/pilgrim/diveintopython3/examples' , 'humansize.py' )
[1]
>>> (dirname , filename ) = os .path .split (pathname ) [2]
>>> dirname
'/Users/pilgrim/diveintopython3/examples'
[3]
>>> filename
'humansize.py'
[4]
>>> (shortname , extension ) = os .path .splitext (filename )
>>> shortname
'humansize'
>>> extension
'.py'
[5]
1. ↑ Функция split дробит полный путь и haращает кортеж, содержащий
отдельно путь до каталога и имя файла.
2. ↑ Помните, я рассказыZeijhlhdZdijbkаиZlvg_kdhevdhagZq_gbc
за раз и как _jgml ь одноj_f_gghg_kdhevdhagZq_gbcbanmgdpbb"
Функция os .path .split () дейстm_lbf_gghlZdFh`ghijbkоить
haращаемое из функции split значение кортежу из дmoi_j_f_gguo
Каждая из переменных примет значение соот_lklующего элемента
результирующего ко ртежа.
3. ↑ Первая переменная — dirname — получит значение перh]hwe_f_glZ
кортежа, haращаемого функцией os .path .split (), а именно путь до
каталога.
4. ↑ Вторая переменная — filename — примет значение lhjh]hwe_f_glZ
кортежа, haращаемого функцией os .path .split (), а именно имя файла.
5. ↑ Модуль os .path также содержит функцию os .path .splitext (), которая
дробит имя файла и haращает кортеж, содержащий отдельно имя и
отдельно расширение файла. Можно использоZlvlm`_l_ogbdmqlhb
ранее для присZbания каждого из интересующих значений отдельным
переменным.

75

Получение содержимого каталога
Модуль glob понимает символы -джокеры,
использующиеся в командных оболочках.
Модуль glob — это ещё один инструмент из стандартной библиотеки Python.
Это простой способ программно получить содержимое папки, а также он умеет
использоZlv симheu -джокеры , с которыми ugZерняка знакомы, если
работали dhfZg^ghckljhd_.
>>> os .chdir ('/Users/pil grim/diveintopython3/' )
>>> import glob
>>> glob .glob ('examples/*.xml' )
['examples \\feed -broken.xml' ,
'examples \\feed -ns0.xml' ,
'examples \\feed.xml' ]
[1]
>>> os .chdir ('examples/' ) [2]
>>> glob .glob ('*test*.py' )
['alphameticstest.py' ,
'pluraltest1.py' ,
'pluraltest2.py' ,
'pluraltest3.py' ,
'pluraltest4.py' ,
'pluraltest5.py' ,
'pluraltest6.py' ,
'romantest1.py' ,
'romantest10.py' ,
'romantest2.py' ,
'romantest3.py' ,
'romantest4.py' ,
'romantest5.py' ,
'romantest6.py' ,
'romantest7.py' ,
'romantest8.py' ,
'romantest9.py' ]
[3]
1. ↑ Модуль glob принимает шаблон, содержащий символы -джокеры, и
haращает пути всех файлов и каталогоkhhlетстmxsbo_fm<wlhf
примере шаблон содержит путь к каталогу и , которому будут "*.xml"
khhl\_lklh\Zlv\k_ -файлы dZlZeh]_ . xml examples

76

2. ↑ Теперь сделаем текущим рабочим каталог . Функция os .chdir () examples
может принимать и относительные пути.
3. ↑ Вы можете использоZlvg_kdhevdhkbfолов -джокеро сhzf
шаблоне. Этот пример находит k_nZceu текущем рабочем каталоге,
заканчивающиеся на и содержащие слово где -нибудь bf_gb .py test
nZceZ
Получение сведений о файле
Любая современная операционная система хранит сведения о каждом файле
(метаданные): дата создания, дата последней модификации, размер файла
и т. д. Python предостаey_l_^bguc программный интерфейс для доступа
к этим метаданным. Вам не надо открыZlvnZce; kzqlhlj_[m_lky — имя
файла.
>>> import os
>>> print (os .getcwd ())
c: \Users \pilgrim \diveintopython3 \examples
[1]
>>> metadata = os .stat ('feed.xml' ) [2]
>>> metadata. st_mtime
1247520344.9537716
[3]
>>> import time [4]
>>> time .localtime (metadata. st_mtime )
time .struct_time (tm_year =2009 , tm_mon =7, tm_mday =13 , tm_hour =17 ,
tm_min =25 ,
tm_sec =44 , tm_wday =0, tm_yday =194 , tm_isdst =1)
[5]
1. ↑ Текущий рабочий каталог — папка с примерами.
2. ↑ — файл iZid_kijbf_jZfb<uah\nmgdpbb os .stat () feed.xml
haращает объект, содержащий различные метаданные о файле.
3. ↑ st_mtime — j_fybaf_g_gbynZceZgh записано оно в ужасно
неудобном формате. (Фактически это количестhk_dmg^ijhr_^rbo
с начала « эры UNIX », начавшейся в перmxk_dmg^m1 янZjy0 года.
Серьёзно.)
4. ↑ Модуль time является частью стандартной библиотеки Python. Он
содержит функции для преобразоZgbcf_`^mjZaebqgufbnhjfZlZfb
предстаe_gbyремени и часоufbihykZfb^eyij_h[jw зоZgbybo
строки ( str ) и др.
5. ↑ Функция time .localtime () преобразует j_fyb~ формата «секунды с
начала эры» (поле st_mtime , haращённое функцией os .stat ()) в более
удобную структуру, содержащую год, месяц, день, час, минуту, секунду

77

и т. д. Этот файл в последний раз изменялся 13 июля 2009 года,
примерно в 17 часоfbgml.
# продолжение предыдущего примера =
>>[ =metadataK st_size =
3070 =
>>[ =import =humansize =
[1]
>>> humansize. approximate_size (metadata. st_size )
'3.0 KiB'
[2]
1. ↑ Функция os .stat () также haращает размер файла в сhcklе st_size .
Размер файла — 3070 байт. feed.xml
2. ↑ Вы можете передать сhcklо st_size в функцию approximate_size ().
Получение абсолютных путей
В предыдущем разделе функция glob .glob () haращала список относительных
путей. В перhfijbf_j_imlbbf_ebид 'examples \feed.xml' , а h lhjhf
относительные пути были даже короче, например, 'romantest1.py' . Пока вы
остаётесь в текущем рабочем каталоге, по этим относительным путям можно
будет о ткрыZlvnZceubebihemqZlvbo метаданные. Но если uaZohlbl_
получить абсолютный путь — то есть тот, который dexqZ_l\k_bf_gZ
каталогов до корнеh]hbeb^h буквы диска, ZfihgZ^h[blkynmgdpby
os .path .realpath ().
>>> import os
>>> print (os .getcwd ())
c: \Users \pilgrim \diveintopython3 \examples
>>> print (os .path .realpath ('feed.xml' ))
c: \Users \pilgrim \diveintopython3 \examples \feed.xml
Генераторы списков
В генераторах списков можно использоZlvex[u_ыражения Python.
С помощью генераторов списков можно легко отобразить один список
в другой, примениg_dhlhjmxnmgdpbxd каждому элементу.
>>> a_list = [1, 9, 8, 4]
>>> [elem * 2 for elem in a_list ]
[2, 18 , 16 , 8]
[1]
>>> a_list
[1, 9, 8, 4]
[2]

78

>>> a_list = [elem * 2 for elem in a_list ]
>>> a_list
[2, 18 , 16 , 8]
[3]
1. ↑ Чтобы понять, что здесь происходит, прочитайте генератор справа
налево. a_list — отображаемый список. Python последоZl_evgh
перебирает элементы списка a_list , j_f_gghijbk\Zbа я значение
каждого элемента переменной elem . Затем применяет функцию elem * 2
и добаey_lj_amevlZly haращаемый список.
2. ↑ Генератор создаёт ноuckibkhdg| изменяя исходный.
3. ↑ Можно присhblvj_amevlZljZ[hlu]_g_jZlhjZkibkdZhlh[jZ`Z_fhc
переменной. Python создаст ноuckibkhdy памяти и, когда результат
работы генератора будет получен, присhbl_z о исходной переменной.
В генераторах списков можно использоZlvex[u_ыражения Python, включая
функции модуля os , применяемые для работы с файлами и каталогами.
>>> import os , glob
>>> glob .glob ('*.xml' )
['feed -broken.xml' , 'feed -ns0.xml' , 'feed.xml' ]
[1]
>>> [os .path .realpath (f) for f in glob .glob ('*.xml' )]
['c: \\Users \\pilgrim \\diveintopython3 \\examples \\feed -broken.xml' ,
'c: \\Users \\pilgrim \\diveintopython3 \\examples \\feed -ns0.xml' ,
'c: \\Users \\pilgrim \\diveintopython3 \\examples \\feed.xml' ]
[2]
1. ↑ Это выражение haращает список k_o .xml -файлов в текущем
рабочем каталоге.
2. ↑ Этот генератор принимает с писок k_o[Pl -файлов и преобразует его
в список полных путей.
При генерироZgbbkibkdh\fh`ghlZd`_nbevljh\Zlvwe_f_gluqlh[u
отбросить некоторые значения.
>>> import os , glob
>>> [f for f in glob .glob ('*.py' ) if os .stat (f).st_size > 6000 ]
['pluraltest6.py' ,
'romantest10.py' ,
'romantest6.py' ,
'romantest7.py' ,
'romantest8.py' ,
'romantest9.py' ]
[1]

79

1. ↑ Чтобы профильтроZlvkibkhd^h[Zьте оператор if в конце
генератора списка. Выражение, стоящее после оператора if, будет
uqbke_gh^eydZ`^h]hwe_f_glZkibkdZ?kebwlh\ujZ`_gb е будет
истинно, данный элемент будет обработан и dexqzgy генерируемый
список. В данной строке генерируется список всех .py -файлов в текущей
директории, а оператор if фильтрует этот список, остаeyylhevdhnZceu
размером больше 6000 байт. Таких файлов то лько шесть, поэтому будет
сгенерироZgkibkhdb~ шести имён файлов.
Все рассмотренные примеры генератороkibkdh использоZebijhklu_
ujZ`_gbymfgh`_gb_qbkeZgZdhgklZglmызов одной функции или просто
haрат элемента списка без изменений (после филь трации).
Но при генерации списков можно использоZlvыражения любой сложности.
>>> import os , glob
>>> [(os .stat (f).st_size , os .path .realpath (f)) for f in glob .glob ('*.xml' )]
[(3074 , 'c: \\Users \\pilgrim \\diveintopython3 \\examples \\feed -broken.xml' ),
(3386 , 'c: \\Users \\pilgrim \\diveintopython3 \\examples \\feed -ns0.xml' ),
(3070 , 'c: \\Users \\pilgrim \\diveintopython3 \\examples \\feed.xml' )]
>>> import humansize
[1]
>>> [(humansize. approximate_size (os .stat (f).st_size ), f) for f in glob .glob ('*.xml' )]
[('3.0 KiB' , 'feed -broken.xml' ),
('3.3 KiB' , 'feed -ns0.xml' ),
('3.0 KiB' , 'feed.xml' )]
[2]
1. ↑ Этот генератор ищет все .xml -файлы l_dms_fjZ[hq_fdZlZeh]_
получает размер каждого файла (uauая функцию os .stat ()), и создает
кортеж из размера файла и абсолютного пути каждого файла (uauая
функцию os .path .realpath ()).
2. ↑ Этот генератор, осноZggucgZij_^u^ms_fызывает функцию
approxima te_size (), передаZy_cjZaf_jdZ`^h]h[Pl -файла.
Генераторы словарей
Генератор словаря похож на генератор списка, но f_klhkibkdZhgkha^Z_l
словарь.
>>> import os , glob
>>> metadata = [(f, os .stat (f)) for f in glob .glob ('*test*.py' )] [1]
>>> metadata [0]
('alphameticstest.py' , nt. stat_result (st_mode =33206 , st_ino =0, st_dev =0,
st_nlink =0, st_uid =0, st_gid =0, st_size =2509 , st_atime =1247520344 ,
[2]

80

st_mtime =1247520344 , st_ctime =1247520344 ))
>>> metadata_dict = {f:os .stat (f) for f in glob .glob ('*test*.py' )} [3]
>>> type (metadata_dict )

[4]
>>> list (metadata_dict. keys ())
['romantest8.py' , 'pluraltest1.py' , 'pluraltest2.py' , 'pluraltest5.py' ,
'pluraltest6.py' , 'romantest7.py' , 'romantest10.py' , 'romantest4.py' ,
'romantest9.py' , 'pluraltest3.py' , 'romantest1.py' , 'romantest2.py' ,
'romantest3.py' , 'romantest5.py' , 'romantest6.py' , 'alphameticstest.py' ,
'pluraltest4.py' ]
[5]
>>> metadata_dict ['alphameticstest.py' ].st_size
2509
[6]
1. ↑ Это не генератор словаря, это генератор списка. Он находит все
файлы с расширением .py, про_jy_lbo имена, а затем создает кортеж
из имени файла и метаданных файла (uauая функцию os .stat ()).
2. ↑ Каждый элемент результирующего списка — кортеж.
3. ↑ Это генератор словаря. Синтаксис подобен синтаксис у генератора
списка, но с дmfyhlebqbyfb<h -перuohgaZdexqzg фигурные
скобки, а не в квадратные. Во -вторых, f_klhh^gh]h\ujZ`_gby
для каждого элемента он содержит дZjZa^_ezggu_^оеточием.
Выражение слева от дh_lhqby y нашем примере f) яey_l ся ключом
словаря; ujZ`_gb_kijZа от дh_lhqby y нашем примере os .stat (f)) —
значением.
4. ↑ Генератор словаря haращает словарь.
5. ↑ Ключи данного слоZjy — это просто имена файлоihemq_ggu_
с помощью glob .glob ('*test*.py' ).
6. ↑ Знач ение, сyaZggh_k каждым ключом, получено с помощью функции
os .stat (). Это означает, что в этом словаре мы можем по имени файла
получить его метаданные. Один из элементов метаданных ( st_size ) — это
размер файла. Размер файла alphameticstest.py — 2509 байт.
Также, как и в генераторах списков, вы можете dexqZlvy генераторы
словарей условие if, чтобы отфильтроZlvходную последоZl_evghklvk
помощью ujZ`_gby -условия, uqbkeyxs_]hky^ey каждого элемента.
>>> import os , glob , humansize
>>> metadata_dict = {f:os .stat (f) for f in glob .glob ('*' )} [1]
>>> humansize_dict =
{os .path .splitext (f)[0]:humansize. approximate_size (meta. st_size ) \
[2]

81

... for f, meta in metadata_dict. items () if meta. st_size > 6000 }
>>> list (humansize_dict. keys ())
['romantest9' , 'romantest8' , 'romantest7' , 'romantest6' , 'romantest10' , 'pluraltest6' ]
[3]
>>> humansize_dict ['romantest9' ]
'6.5 KiB'
[4]
1. ↑ В этом ujZ`_gbb[_jzlkykibkhdnZceh\y текущей директории
(glob .glob ('*' )), для каждого файла определяются его метаданные
(os .stat (f)) и строится словарь, ключами которого ukl упают имена
файлов, а значениями — метаданные каждого файла.
2. ↑ Этот генератор строится на основе предыдущего. Отфильтроu\Zxlky
файлы меньше 6000 байт ( if meta. st_size > 6000 ). Отобранные элементы
используются для построения словаря, ключами которого яeyxlky
имена файлов без расширения ( os .path .splitext (f)[0]), а значениями —
приблизительный размер каждого файла
(humansize. approximate_size (meta. st_size )).
3. ↑ Как Zfm`_ba\_klghb~ предыдущего примера, всего имеется шесть
таких файлов, следовательно, в этом словаре шесть элементов.
4. ↑ Значение для каждого ключа предстаey_lkybak_[ykljhdm
полученную uahом функции . approximate_size()
Другие интересные штуки, которые можно делать с помощью
генераторов слова рей
Вот трюк с генераторами словарей, который когда -нибудь может оказаться
полезным: перестаноdZf_klZfbdexq_cbagZq_gbckeh\Zjy.
>>> a_dict = {'a' : 1, 'b' : 2, 'c' : 3}
>>> {value:key for key , value in a_dict. items ()}
{1: 'a' , 2: 'b' , 3: 'c' }
Конечно же, это сработает, только если значения элементов словаря
неизменяемы, как, например, строки или кортежи.
>>> a_dict = {'a' : [1, 2, 3], 'b' : 4, 'c' : 5}
>>> {value:key for key , value in a_dict. items ()}
Traceback (most recent call last ):
File "" , line 1, in
File "" , line 1, in
TypeError : unhashable type : 'list'

82

Генераторы множеств
Нельзя остаblvaw бортом и множестZhgblh`_fh]mlkha^Z\Zlvky
с помощью генераторо?^bgklенное отличие — f_klhiZjdexqagZq_gb_
они строятся на осно_h^gboagZq_gbc.
>>> a_set = set (range (10 ))
>>> a_set
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> {x ** 2 for x in a_set }
{0, 1, 4, 81 , 64 , 9, 16 , 49 , 25 , 36 }
[1]
>>> {x for x in a_set if x % 2 == 0}
{0, 8, 2, 4, 6}
[2]
>>> {2**x for x in range (10 )}
{32 , 1, 2, 4, 8, 64 , 128 , 256 , 16 , 512 }
[3]
1. ↑ В качест_ходных данных генераторы множеств могут получать
другие множества. Этот генератор рассчитыZ_ld\Z^jZlufgh`_klа
чисел в диапазоне от 0 до 9.
2. ↑ Подобно генераторам списков и словарей, генераторы множестfh]ml
содержать условие if для про_jdbdZ`^h]hwe_f_gl а перед dexq_gb_f
его в результирующее множестh.
3. ↑ На oh^]_g_jZlhjufgh`_kl\fh]mlijbgbfZlvg| только множестZ
но и любые другие последоZl_evghklb.
Материалы для дальнейш его чтения
 Модуль os
 os — доступ к особым возможностям операционных систем
 Модуль os.path
 os.path — манипуляции с именами файлов, независимые от платформы
 Модуль glob
 glob — сравнение имён файлов с шаблонами
 Модуль time
 time — Функции для манипулиров ания временем
 Генераторы списков
 Вложенные генераторы с писков
 Техника циклов

83

Строки

Немного скучных вещей, которые вам необходимо знать перед
погружением
Знаете ли uqlhmgZjh^Zhkljhа БугенbevkZfucdhjhldbcZenZит 
мире? Алфавит языка ротокас состоит всего из 12 бук$(*,.2356
T, U, и V. На другом конце этой сh_h[jZaghcqbkeh\hchkbjZkiheh`bebkv
такие языки, как китайский, японс кий и корейский, насчитыZxsb_lukyqb
симheh. Английский, конечно, содержит всего 26 букв — 52, если считать
буквы и _jog_]hbgb`g_]hj_]bkljh — плюс горстка знаков
пунктуации !@#$%&.
Когда люди гоhjyll_dklhgbih^jZamf_ают «буквы и симheugZ экране
компьютера». Но компьютеры не работают с буквами и симheZfbhgb
работают с битами и байтами. Каждый фрагмент текста, который udh]^w -
либо b^_ebgZwdjZg_dhfivxl_jZgZkZfhf^_e_ojZgblky\hij_^_e_gghc
кодироd_=jm[h]hоря, кодироdZkbfол оh[_ki_qbает соот_lklие того,
что вы b^bl_gZwdjZg_blh]hqlhgZkZfhf^_e_ojZgblky\iZfylbbebgZ
диске. Сущестm_lfgh]hjZaebqguodh^bjhок симheh, некоторые из них
оптимизироZgu^eydhgdj_lguoyaudh\gZijbf_jjmkkdh]hdblZckdh]hbeb
анг лийского, другие могут быть использоZgukjZam^eyg_kdhevdboyaudh.
В дейстbl_evghklbсе гораздо сложнее. Многие симheuyляются общими
для нескольких кодироhdghdZ`^Zydh^bjhка может использоZlvk\hx
последоZl_evghklv[Zclh\^eyojZg_gbybo памяти или на диске. Вы
можете думать о кодироd_kbfолоdZdhjZaghидности
криптографического ключа. Всякий раз, когда Zfi_j_^Zxl
последоZl_evghklv[Zclh\ — файл, _x -страницу, k_jZно — и
ут_j`^Zxlqlhwlhl_dklам необходимо понять, какая кодироdZ
использоZeZkvAgZydh^bjhку, ukfh`_l_^_dh^bjhать байты kbf\heu
Если Zf^Zxlg_ijZильный ключ или не дают ключа hсе, Zfg_hklZ_lky
ничего, кроме как попытаться взломать код самостоятельно. Скорее k_]h
результате вы получите ку чу крякозябро JLEEHULVK — тарабарщина,
неgylgZyj_qvghlZdihgylg__ijbfi_j_\ <k_qlhы знали о строках —
не_jgh.
Все, что вы знали о строках — неверно.

84

Несомненно, Zfijboh^behkvидеть такие _x -страницы, со странными
hijhkbl_evgufbagZdw ми на месте апострофоH[uqghwlhhagZqZ_lqlh
аlhjkljZgbpug_ijZильно указал их кодироdmашему браузеру осталось
просто угадать ее, а результатом стала смесь ожидаемых и со_jr_ggh
неожиданных симheh. В английском языке это просто раздражает; в других
языках результат может стать со_jr_gghg_qblZ_fuf.
Сущестmxldh^bjhки для k_ohkghных мироuoyaudh. Но, поскольку,
языки сущест_gghhlebqZxlky^jm]hl^jm]ZZiZfylvb^bkdh\h_
пространстhjZgvr_[ueb^hjh]bfbdZ`^Zydh^bjhка оптимизиров ана для
конкретного языка. Под этим я подразумеZxlhqlh^eyij_^klZления
симheh сh_]hyaudZ\k_dh^bjh\dbbkihevamxlh^bgblhl`_^bZiZahg
чисел (0 -255). Например, uероятно знакомы с кодироdhc$6&,,dhlhjZy
хранит симheuZg]ebckdh]hyaudZ b^_qbk_ehl^h  — заглаgZy
«A», 97 — строчная «a» и т.д.) Английский алфаblhq_gvijhklhcbfh`_l
быть предстаe_gf_g__q_fqbkeZfb?keb\Zfbaестна дhbqgZy
система счисления, uihgbfZ_l_qlh\[Zcl_aZ^_cklоZghсего 7 битоba
8.
Западноеjhi_ckdb_yaudblZdb_dZdnjZgpmakdbcbkiZgkdbcbg_f_pdbc
содержат больше симheh, чем английский. Точнее, они содержат симheuk
различными диакритическим знаками, например, испанский симhexKZfZy
распространенная кодироdZ^eywlboyau ков — CP -1252, также из_klgZydZd
«windows -1252», что сyaZghkrbjhdbfbkihevahанием ее в операционной
системе Microsoft Windows. В кодироd_&P -1252 симheukghf_jZfbhl^h
127 такие же, как и $6&,,ZhklZevghc^bZiZahgbkihevam_lky^eylZdbo
сим heh как n -с-тильдой -с_jom  u -с-дmfy -точками -с_jom  bl^
Однако, это k__s_h^gh[ZclgZydh^bjhка; максимально hafh`gucghf_j
255 еще помещается h^bg[Zcl.
А еще сущестmxllZdb_yaudbdZddblZckdbcyihgkdbcbdhj_ckdbcdhlhju_
имею т так много симheh, что они требуют многобайтоuodh^bjhок. Это
означает, что каждый «симheij_^klZляется дmo[Zclhым числом от 0 до
65535. Но различные многобайтные кодироdbсё равно имеют ту же
проблему, что и различные однобайтные кодироdbdw ждая кодироdZ
использует одинаковые числа для обозначения разных _s_cHlebqb_ebrv
lhfqlh^bZiZahgqbk_e[hevr_ihlhfmqlhgm`ghdh^bjhать намного
больше симheh\.
Это было iheg_ghjfZevgh\g_k_l_ом мире, где вы набирали «текст» для
себя и иног да распечатыZeb_]h<wlhffbj_g_[uehgbq_]hdjhf_
«обычного текста» (не знаю, м.б. быть hh[s_g_i_j_одить “plain text” —
прим. пере Bkoh^gucdh^[ue кодироd_$6&,,Z^eyсего остального
использоZebkvl_dklh\u_ijhp_kkhjudhlhju_hij_^_ey ли сhb
собст_ggu_ g_l_dklh\u_ nhjfZlu\dhlhjuogZjy^mkbgnhjfZpb_ch
форматироZgbbhlke_`bалась и информация о кодироd_Ex^bqblZxlwlb
документы с помощью такого же текстоh]hijhp_kkhjZdZdhcbkihevah\Zeky
и для их создания, так что, все бо лее или менее работало.

85

Теперь подумайте о распространении глобальных сетей, таких как e -mail и
web. Множестhh[uqgh]hl_dklZi_j_f_sZ_lkyокруг планеты, создаётся
на одном компьютере, передаётся через второй и принимается и
отображается третьим компью тером. Компьютеры b^yllhevdhqbkeZgh
числа могут иметь различное значение. О нет! Что же делать? Системы были
спроектированы таким образом, чтобы передаZlvbgnhjfZpbxhdh^bjhке
f_kl_kdZ`^ufhljuком «обычного текста». Вспомните, это
криптографиче ский ключ, устанаebающий соот_lklие между числами и
понятными человеку симheZfbIhl_jyggucdexqhagZqZ_lbkdZ`_ggucl_dkl
или кракозябры, если не хуже.
Теперь подумайте о задаче хранения различных отрыdh текста в одном
месте, например в одной табли це базы данных, хранящей k_dh]^w -либо
полученные ZfbHPDLOkhh[s_gby<Zfсё ещё нужно хранить кодировку
f_kl_kdZ`^ufhljuком текста, чтобы иметь hafh`ghklvijhqblZlv_]h
Думаете, это трудно? Попробуйте реализоZlvihbkd этой базе данных, с
пре образоZgb_fgZe_lmf_`^mfgh`_kl\hfdh^bjh\hdJZaе это не
забаgh?
Теперь подумайте о hafh`ghklbfgh]hyauqguo^hdmf_glh\]^_kbfолы из
нескольких языкоgZoh^ylkyjy^hf одном документе. (Подсказка:
программы, которые пытаются делать это, обычно используют коды смены
алфаblZ^eyi_j_dexq_gbyj_`bfh». Бац, и вы в русском режиме KOI8 -R ,
и 241 означает Я; бац, и теперь u\]j_q_kdhfj_`bf_^ey0DFLQWRVKb
означает ώ.) И конечно ulZd`_aZohlbl_hkms_klлять поиск по этим
документам.
Теперь плачьте, т.к. все, что uagZebhkljhdZo — неверно, и нет такого
понятия «обычный текст».
Юникод
В_^_gb_ Юникод.
Юникод спроектироZg^eyij_^klZ\e_gbykbkl_fhcdZ`^h]hkbfола любого
языка. Юникод предстаey_ldZ`^mx[md\mkbfол или идеографию как 4-х
байтное число.Каждое число предстаey_lmgbdZevguckbfол, используемый
по крайней мере в одном из языков fbj_ bkihevamxlky[hevr_q_f
из них, таким образом 2 байта не были бы достаточны.) У симheh, которые
используются jZaguoyaudZoh^b н код, если нет хорошей этимологической
причины. Незаbkbfhhlсего, есть точно 1 код сот_lklующий симhemb
точно 1 симhekhhlетстmxsbcqbkeh\hfmdh^mDZ`^ucdh^\k_]^ZhagZqZ_l
только один симheg_lgbdZdboj_`bfh». U+0041 всегда соот_lklуе т 'A',
даже если \Zr_fyaud_g_lmkbfола 'A'.
На перuc\a]ey^wlhеликолепная идея. Одна кодироdZ^ey\k_]h
Множестhyaudh\ одном документе. Не надо больше никаких
«переключений режимо^eykf_gudh^bjhок. Но у Zk^he`_gозникнуть

86

очеb^guc hijhkQ_luj_[ZclZ"GZdZ`^uckbfол? Это кажется ужасно
расточительным, особенно для таких языкоdZdZg]ebckdbcbebbkiZgkdbc
которых нужно меньше одного байта (256 чисел) для предстаe_gbyex[h]h
hafh`gh]hkbfола. На самом деле, это расточительн о даже для
иероглифических языко lZdbodZddblZckdbc  которых никогда не нужно
больше, чем дZ[ZclZgZkbfол.
Сущестm_ldh^bjhка Юникод, которая использует четыре байта на симhe
Она назыZ_lky87F -32, так как 32 бита = 4 байтам. UTF -32 — прямоли нейная
кодироdZdZ`^hfmkbfолу Юникод (4 -байтоhfmqbkem khhlетстm_l
симhekhij_^_e_ggufghf_jhfWlhbf__lkои преимущестZkZfh_
Z`gh_badhlhjuoaZdexqZ_lky\lhfqlhы можете найти N -ый симhe
строке за постоянное j_fylZddZdN -ый сим hegZqbgZ_lky\
1[Zcl_Gh
эта кодироdZlZd`_bf__lbg_^hklZldbkZfuchq_идный из которых — для
хранения каждого симheZlj_[m_lkyq_luj_[ZclZ.
Хотя в Юникоде сущестm_lh]jhfgh_dhebq_klо симheh, на самом деле
большинство людей никогда не испо льзуют те, номера которых ur_
(2^16). Поэтому сущестm_l^jm]Zydh^bjhка Юникода, назыZ_fZy87F -16
(очеb^ghqlh[bl [ZclZf 87F -16 кодирует каждый симhe\ghf_jZo
0–65535; для предстаe_gby`_j_^dhbkihevam_fuoaZij_^_evguo
симheh с номерами ur_ijboh^blkyijb[_]Zlvdg_dhlhjufmeh\dZf
Самое очеb^gh_ij_bfms_klо: UTF -16 дZ`^uwnn_dlb\g__ih
потреблению памяти, нежели UTF -32, так как каждый симhelj_[m_l[ZclZ
f_klh4 -х (кроме случаеkl_fbkZfufbaZij_^_evgufbkbfо лами). И, как
и kemqZ_k87F -32, можно легко отыскать нужный N -ый симhe\kljhd_aZ
постоянное j_fy_kebы у_j_guqlhl_dklg_kh^_j`blaZij_^_evguo
симheh; и всё хорошо, если это дейстbl_evghlZd.
Тем не менее сущестmxlg_hq_идные недостатк и, как UTF -32, так и UTF -16.
На различных компьютерных платформах отдельные байты хранятся по -
разному. Это означает, что симhe8('fh`_l[ulvkhojZgzg UTF -16
либо как 4E 2D, либо как 2D 4E (задом наперёд), в заbkbfhklbhl
используемого порядка байт: big -endian или little -engian. (Для UTF -32 b^h\
порядко^Z`_[hevr_ IhdZы храните свои документы исключительно у
себя на компьютере, вы в безопасности — различные приложения на одном
компьютере всегда используют один и тот же порядок. Но lhlfhf_gl , когда
uaZohlbl_i_j_^Zlv^hdmf_glugZ^jm]hcdhfivxl_j Интернете или иной
сети, ZfihgZ^h[blkykihkh[ihf_ldb^hdmf_glZdZdhcmас используется
порядок байт. В протиghfkemqZ_ijbgbfZxsZy^hdmf_glkbkl_fZg_bf__l
понятия, что предстаey_lihke| доZl_evghklv[Zcl('8('beb
U+2D4E.
Для решения этой проблемы многобайтоu_dh^bjhки Юникода имеют
“отметку о порядке байт” (BOM), которая предстаey_lkh[hcki_pbZevguc
непечатный симhedhlhjuc\ufh`_l_ключить в начало документа для
сохра нения информации о используемом порядке байт. Для UTF -16, эта
отметка имеет номер U+FEFF. Если uijbgbfZ_l_^hdmf_glk87F -16,

87

который начинается с байт FF FE, — это однозначно опо_sZ_lhijyfhf
порядке; если же начинается с байт FE FF, следовательно пор ядок обратный.
На самом деле, UTF -16 не идеален, особенно если ubf__l_^_ehk[hevrbf
количестhfkbfоло$6&,,<ug_^mfZebhlhfqlh^Z`_dblZckdZyеб -
страница может содержать большое количестhkbf\heh\$6&,, — k_
элементы и атрибуты, окружающие печатные китайские симheu bgZgbo
тоже тратится 2 байта, хотя они и умещаются в один). Возможность искать N -
ый симhe\kljhd_aZihklhyggh_\j_fyaZfZgqbа, однако до сих пор
сущестm_lgZ^h_шая всем проблема с теми "запредельными" симheZfb
которая заключается lhfqlhы не можете гарантироZlvqlhdZ`^uc
симheojZgblkylhqgh дmo[ZclZoследстb_q_]hihbkdaZihklhyggh_
j_fylZd`_klZghится неhafh`guf _kebы только не имеете отдельный
индекс по симheZf Hldjhxам секрет: и до сих п ор fbj_kms_klует
огромное число ASCII тексто.
Кое -кто до Zklh`_aZ^mfuался над этой проблемой и пришёл hldlZdhfm
решению:
UTF -8
UTF -8 это кодировка Юникода с переменным числом байт. Это означает, что
различные симheuaZgbfZxljZagh_qbkeh[Zc т. Для симheh ASCII (A -Z,
цифр и т.п.) UTF -8 использует только 1 байт на симhe ^_cklительно, а
больше и не требуется). Причём и на деле для них зарезерbjhаны точно те
самые номера, как и в ASCII; перu_kbfолов (0 –127) таблицы UTF -8
неотличимы от той же части ASCII. “Расширенные” симheulZdb_dZdxb|
занимают дZ[ZclZ (bytes are not simply the Unicode code point like they would
be in UTF -16; there is some serious bit -twiddling involved.) Китайские симheu
такие как 中 занимают три байта. Самые редко используемые симheu —
четыре.
Недостатки: так как каждый симheaZgbfZ_ljZaebqgh_qbkeh[ZclihbkdN -го
симheZh[eZ^Z_lkeh`ghklvx2 1 qlhhagZqZ_lqlhремя поиска
пропорционально длине строки. Кроме того, bit -twidd ling, применяемый для
кодироZgbykbfоло байты, также у_ebqbает время поиска. (прим.
пере\dh^bjhке с фиксированным числом байт на симheремя поиска
состаey_l2  lh_klvhghg_aZисит от длины строки).
ПреимущестZdjZcg_wnn_dlb\gh_dh^b роZgb_gZb[he__qZklh
используемых симheh ASCII. Не хуже, чем хранение расширенных симheh
87F -16. Лучше, чем UTF -32 для китайских симheh. Также (не хочу грузить
ZkfZl_fZlbdhclZdqlhам придётся по_jblvfg_gZkeh\h \kязи с
самой природой bit twiddling, проблемы с порядком байт просто не сущестm_l
Документ, закодироZgguc\87F -8, использует один и тот же порядок байт на
любом компьютере!

88

Погружение
В языке программироZgby3\WKRQсе строки предстаeyxlkh[hc
последоZl_evghklv8QLFRd e симheh. В Python нет такого понятия, как
строка в кодироd_87F -8, или строка dh^bjhке CP -1251. Некорректным
яey_lkyопрос: "Это строка 87F -8?" UTF -8 — это способ закодироZlv
симheu\ihke_^hательность байт. Если Вы хотите aylvkljhdmb
прев ратить её ihke_^hательность байт в какой -либо кодироd_lh3\WKRQ
может помочь Вам wlhf?keb`_<u`_eZ_l_ij_\jZlblv
последоZl_evghklv[Zcl строку, то и здесь Python 3 Вам пригодится. Байты
— это не симheu[Zclu — это байты. Симheu — это аб стракция. А строка
— это последоZl_evghklvlZdboZ[kljZdpbc.
>>> s = '深入 Python' ①
>>> len (s) ②
9
>>> s[0] ③
'深 '
>>> s + ' 3' ④
'深入 Python 3'
 ① Чтобы создать строку окружите её кавычками. В Python строки можно
создавать как с помощью одинарных ( ), так и с помощью дhcguo '
dZ\uq_d ). "
 ② Стандартная функция len () ha\jZsZ_l^ebgmkljhdbl_dhebq_klо
симheh в ней. Эта же функция используется для определения длины
списков, кортежей, множеств и словарей. Строка ^ZgghfkemqZ_ihoh`Z
на кортеж симheh.
 ③ Так же как и со списками, Вы можете получить произhevgu й симhe
из строки, зная его индекс.
 ④ Так же как и со списками, Вы можете объединять строки, используя
оператор . +
Форматирование строк
Строки можно создавать как с помощью одинарных, так и с помощью двойных
кавычек.
Давайте взглянем еще раз на : humansize.py
SUFFIXES = {1000 : ['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ], ①
1024 : ['KiB' , 'MiB' , 'GiB' , 'TiB' , 'PiB' , 'EiB' , 'ZiB' , 'YiB' ]}

def approximate_size (size , a_kilobyte_is_1024_bytes =True ):

89

'''Convert a file size to human -readable form. ②

Keyword arguments:
size -- file size in bytes
a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
if False, use multiples of 1000

Returns: string

''' ③
if size < 0:
raise ValueError ('number must be non -negative' ) ④

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
for suffix in SUFFIXES [multiple ]:
size / = multiple
if size < multiple:
return '{0:.1f} {1}' .format (size , suffix ) ⑤

raise ValueError ('number too large' )
 ① 'KB', 'MB', 'GB'… - это все строки.
 ② Комментарии к функции - это тоже строка. Комментарии к функции
могут быть многострочными, поэтому используются тройные кавычки
gZqZe_b\dhgp_kljhdb.
 ③ Эти тройные каuqdbaZdZgqbают комментарии к функции.
 ④ Здесь еще одна строка, которая передается к онструктору исключения
как удобочитаемый текст ошибки.
 ⑤ Здесь … ого, это ещё что такое?
Python 3 поддержиZ_lnhjfZlbjhание значений kljhdbNhjfZlbjhание
может dexqZlvhq_gvkeh`gu_ыражение. Самое простое использоZgb_ -
это klZка значения ihe е подстаноdbkljhdb.
>>> username = 'mark'
>>> password = 'PapayaWhip' ①
>>> "{0}'s password is {1}" .format (username , password ) ②
"mark's password is PapayaWhip"
 ① Вы же не думаете, что мой пароль дейстbl_evgh PapayaWhip
 ② Здесь много чего происходит. Во перuoызыZ_lkyf_lh^ format(…)
для строки. Строки - это объекты, а у объекто_klvf_lh^u<hторых,
значением всего выражения будет строка. В третьих, и яeyxlky {0} {1}
iheyfbdhlhju_aZf_gyxlkyZj]mf_glZfbi_j_^Zg gufbf_lh^m format()

90

Составные имена полей
Предыдущий пример показал простейший способ форматироZgbykljhdihey
kljhd_ij_^klZ\eyxlbak_[yp_eu_qbkeZWlbqbkeZ фигурных скобках
означают порядкоu_ghf_jZZj]mf_glh\\kibkd_iZjZf_ljZof_lh^Z
. Это означает, что заменяется перufZj]mf_glhf  данном format() {0}
kemqZ_f Z заменяется на lhjhcZj]mf_gl( ), &c. Вы username {1} password
fh`_l_bf_lvklhevdhghf_jhihe_ckdhevdhZj]mf_glh\_klvmf_lh^Z
:Zj]mf_glh\fh`_l[ulvkdhevdh m]h^ghGhbf_gZihe_c]hjZa^h format()
[he__fhsgucbgkljmf_glq_ffh`_lihdZaZlvkygZi_juca]ey^
>>> import humansize
>>> si_suffixes = humansize. SUFFIXES [1000 ] ①
>>> si_suffixes
['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ]
>>> '1000{0[0]} = 1{0[1]}' .format (si_suffixes ) ②
'1000KB = 1MB'
 ① Вместо того, чтобы uauать какие -либо функции модуля , humansize
Вы просто используете один из слоZj_cdhlhju_\wlhffh^me_
определены: список суффиксоKB kl_i_gb)
 ② Этот кусок выглядит сложным, хотя это и не так. ссылается на {0}
i_jucZj]mf_gli_j_^Zggucf_lh^m (переменная format()
Gh - это список. Поэтому ссылается si_suffixes si_suffixes {0[0]}
gZi_jucwe_f_glwlh]hkibkdZ 'KB' . В тоже j_fy ссылается на {0[1]}
lhjhcwe_f_gllh]h`_kibkdZ 'MB' . Все, что находится за фигурными
скобками - dexqZyagZdjZенстZbijh[_eu - остается
нетронутым. В результате мы получим строку '1000KB = 1MB' .
{0} is replaced by the 1st format() argument.
{1} is replaced by the 2nd.
Этот пример показыZ_lqlhijbnhjfZlbjhании bf_gZoihe_cfh`gh
получить доступ к элементам и сhcklам структур данных используя (почти)
Python синтаксис. Это назыZ_lkykhklZ\gu_bf_gZihe_cKe_^mxsb_
составные имена полей пр осто работают:
 Передать список и получить доступ к элементу списка по его индексу (как
ij_^u^ms_fijbf_j_ ;
 Передать словарь и получить доступ к значению слоZjyih_]hdexqm;
 Передать модуль и получить доступ к его переменным и функциям зная
их имена;

91

 Передать экземпляр класса и получить доступ к его свойстZfbf_lh^Zf
по их именам;
 Любая комбинация ur_i_j_qbke_gguo.
И чтобы ahjать Zrfha]от пример которые использует k_
ur_i_j_qbke_ggu_озможности:
>>> import humansize
>>> import sys
>>> '1MB = 1000{0.modules[humansize].SUFFIXES[1000][0]}' .format (sys )
'1MB = 1000KB'
Вот как это работает:
Модуль содержит информацию об работающем интерпретаторе Python. sys
LZddZd<u_]hbfihjlbjhZeblhfh`_l_bkihevahZlv\dZq_kl_Zj]mf_glZ
f_lh^Z Lh_klvihe_ ссылается на модуль . sys .modules format() {0} sys
предстаey_lbak_[ykeh\Zjvkhсеми модулями, которые на данный момент
импортироZgubgl_jij_lZlhjhf3\WKRQDexqbwlh]hkeh\Zjy - это строки с
именами модулей; значения - объекты, предстаe яющие импортироZggu_
модули. Таким образом поле ссылается на словарь {0.modules}
bfihjlbjhZgguofh^me_c sys .modules ['humansize' ] - это объект,
предстаeyxsbckh[hcfh^mev , который Вы только что humansize
bfihjlbjhZebLZdbfh[jZahfkhklZgh_ihe_ {0.modules[humansize]}
ссылается на модуль . Заметьте, что синтаксис здесь отличается. В humansize
kbglZdkbk_3\WKRQdexqbkeh\Zjy sys .modules яeyxlkykljhdZfbbqlh[u
обратиться к значениям словаря необходимо окружить кавычками имя модуля
(например 'huma nsize' ). Вот цитата из PEP 3101: Расширенное
форматироZgb_kljhdIjZила для парсинга ключей очень простые. Если он
начинается с цифры, то его нужно интерпретироZlvdZdqbkehBgZq_ - это
строка". sys .modules ['humansize' ].SUFFIXES - это словарь, опреде ленный 
самом начале модуля . Поле humansize {0.modules[humansize].SUFFIXES}
ссылается на этот словарь.
sys .modules ['humansize' ].SUFFIXES [1000 ] - это список суффиксоkbkl_fuKB
['KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ]. Таким образом поле
ссылается на этот список. А {0.modules[humansize].SUFFIXES[1000]}
sys .modules ['humansize' ].SUFFIXES [1000 ][0] - это первый элемент списка
суффиксо 'KB' . Таким образом окончательное состаgh_ihe_
заменяется на строку из дmo {0.modules[humansize].SUFFIXES[1000][0]}
kbfheh.%
Описатели формата
Постойте! Есть еще кое -что. Давайте a]eyg_fgZ_s_h^gmkljZggmxkljhdmba
: humansize.py

92

if size < multiple:
return '{0:.1f} {1}' .format (size , suffix )
заменяется на lhjhcZj]mf_glf_lh^ZIRUPDW lh_klvgZagZq_gb_ {1}
i_j_f_gghc GhqlhhagZqZ_l "A^_kv^__sb dhlhjhc suffix {0:.1f} {0}
<um`_agZ_l_b hdhlhjhc<u_s_g_keurZeb<lhjZyiheh\bgZ :.1f
^h_lhqb_b\k_qlhihke_g_]h hibkuZ_lnhjfZ ldhlhjucmlhqgy_ldZdbf
h[jZahfaZf_sZxs__agZq_gb_^he`gh[ulvhlnhjfZlbjhZgh
☞ Описатель формата позhey_l<Zffh^bnbpbjhать замещающий текст
многими полезными способами, как функция yaud_ printf()
ijh]jZffbjhZgby&<ufh`_l_^h[ZblvaZiheg_gb_gmeyfbbeb
ijh[_eZfb]hjbahglZevgh_ujZ\gbZgb_l_dklZdhgljhebjhZlv^_kylbqgmx
lhqghklvb^Z`_dhg_jlbjhZlvqbkeZ\ -ричную систему.
Внутри замещаемого поля симhe^оеточие ( ) и k_qlh идет после него :
hagZqZ_lhibkZl_evnhjfZlZHibkZl_evnhjfZlZ означает "округлить ".1"
^h^_kyluo lh_klvihdZauZlvlhevdhh^bgagZdihke_aZiylhc HibkZl_ev
nhjfZlZ означает "число с фиксироZgghcaZiylhc( fixed -point number) (в "f"
hlebqb_hlwdkihg _gpbZevgh]hbebdZdh]h -либо другого предстаe_gby
десятичных чисел). Таким образом если переменная имеет значение size
Z - 'GB' , форматироZggZykljhdZihemqblky '698.2 GB' , потому suffix
qlhqbkehhdjm]e_gh^hh^gh]hagZdZihke_aZiylhcbd нему добаe_g
суффикс.
>>> '{0:.1f} {1}' .format (698.24 , 'GB' )
'698.2 GB'
За всеми деталями описателей формата обратитесь в раздел "Format
Specification Mini -Language" официальной документации Python 3.
Другие общие методы строк
Помимо форматироZgby строки позheyxl^_eZlvfgh`_klо полезных
трюков.
>>> s = '''Finished files are the re - ①
... sult of years of scientif -
... ic study combined with the
... experience of years.'''
>>> s. splitlines () ②
['Finished files are the re -',
'sult of years of scientif -',
'ic study combined with the' ,
'experience of years.' ]

93

>>> print (s. lower ()) ③
finished files are the re -
sult of years of scientif -
ic study combined with the
experience of years.
>>> s. lower ().count ('f') ④
6
 ① В интерактивной оболочке Python Вы можете одить многострочный
текст. Такой текст начинается с тройного симheZdZ\uq_d:dh]^Z<u
нажмете ENTER интерактиgZyh[hehqdZij_^eh`bl<Zfijh^he`blv
одить текст. ЗаканчиZlvkyfgh]hkljhqg ый текст должен также
тройным симhehfdZ\uq_dDh]^Z<ugZ`f_l_(17(5bgl_jZdlb\gZy
оболочка Python выполнит команду (запишет текст в переменную ). s
 ② Метод splitlines () берет многострочный текст и ha\jZsZ_lkibkhd
строк, по одной на каждую строку оригинального текста. Заметьте, что
симheui_j_ода строки не добавляются j_amevlbjmxsb_kljhdb.
 ③ Метод lower () переh^bl\k_kbfолы строки gb`gbcj_]bklj
(Аналогично м етод upper () переh^blkljhdm _jogbcj_]bklj)
 ④ Метод count () подсчитывает количестhihyлений подстроки. Да, 
этом предложении 6 букв "f".
Вот еще один часто klj_qZxsbckykemqZcImklvm<Zk_klvkibkhdiZjdexq -
значение иде , и Вы хотите разделить их и key1=value1&key2=val ue2
ihemqblvkeh\Zjvb^_ {key1: value1 , key2: value2 }.
>>> query = 'user=pilgrim&database=master&password=PapayaWhip'
>>> a_list = query. split ('&' ) ①
>>> a_list
['user=pilgrim' , 'database=master' , 'password=PapayaWhip' ]
>>> a_list_of_lists = [v. split ('=' , 1) for v in a_list ] ②
>>> a_list_of_lists
[['user' , 'pilgrim' ], ['database' , 'master' ], ['password' , 'PapayaWhip' ]]
>>> a_dict = dict (a_list_of_lists ) ③
>>> a_dict
{'password' : 'PapayaWhip' , 'user' : 'pilgrim' , 'database' : 'master' }
 ① Метод принимает один аргумент, разделитель, и разбиZ_l split()
kljhdmihjZa^_ebl_eyfgZkibkhdkljhd<^ZgghfkemqZ_jZa^_ebl_e_f
uklmiZ_lZi_jkZg^ ghjZa^_ebl_evfh`_l[ulv dZdbfm]h^gh &
 ② Теперь у Вас есть список строк, каждая из которых состоит из ключа,
знака и значения. Мы можем использоZlv]_g_jZlhjukibkdh\qlh[u =
ijhclbkvih\k_fmkibkdmbjZa[blvdZ`^mxkljhdmf_kl_i_jh]hagZdZ
на д_kljhdbdexqbagZq_gb_ (Теоретически значение также может =

94

содержать знак ра_gklа. Если просто сделаем 'key=value=foo' .split ('=' )),
то получим список из трех элементо ['key' , 'value' , 'foo' ].)
 ③ Наконец Python может преjZlblvwlhlkibkhd\keh\Zjvbkihevamy
функцию dict().
☞ Предыдущий пример похож на грамматический разбор параметро\85/\
реальной жизни такой разбор намного сложнее. Если Вам необходимо
работать с параметрами URL, то лучше использоZlvnmgdpbx
urllib .parse .parse_qs (), которая умеет обрабатыZlvg_dhlhju_ неочеb^gu_
специфические случаи.
Разрезание строк
Как только Вы создали строку, Вы можете получить любую её часть как ноmx
строку. Это называется разрезание строк. Разрезание работает также как
срезы для списков, что iheg_eh]bqghlZddZdkljhdbwlhl| же
последоZl_evghklbkbfоло.
>>> a_string = 'My alphabet starts where your alphabet ends.'
>>> a_string [3:11 ] ①
'alphabet'
>>> a_string [3:-3] ②
'alphabet starts where your alphabet en'
>>> a_string [0:2] ③
'My'
>>> a_string [:18 ] ④
'My alphabet starts'
>>> a_string [18 :] ⑤
' where your alphabet ends.'
 ① Вы можете получить любую часть строки, так назыZ_fuckj_a
указа^а индекса. ВозjZsZ_fh_agZq_gb_ij_^klZляет из себя
ноmxkljhdmkh^_j} ащую k_kbfолы оригинальной строки в том же
порядке, начиная с перh]hmdZaZggh]hbg^_dkZ.
 ② Как и при работе со срезами списков, индексы для срезоkljhdfh]ml
быть отрицательными.
 ③ Индексация симheh kljhd_gZqbgZ_lkykgmeyihwlhfm
a_string [0:2] вернет перu_^а элемента строки, начиная с a_string [0]
(dexqbl_evgh baZdZgqb\Zy g_ключительно) a_string [2].
 ④ Если срез начинается с индекса 0, то этот индекс можно опустить.
Таким образом a_string [:18 ] - это тоже самое, что и a_string [0:18 ].
 ⑤ Ан алогично, если последний индекс - это длина строки, то его можно
не стаblvLh_klv a_string [18 :] означает тоже самое, что и
a_string [18 :44 ], так как в строке 44 симheZA^_kvgZ[ex^Z_lkyijbylgZy
симметрия. В нашем примере строка содержит 44 симheZ a_ string [:18 ]
haращает перu_kbfолов, а a_string [18 :] haращает все кроме

95

перuokbfолоNZdlbq_kdb a_string [:n ] всегда haращает перu_Q
симheh, а a_string [n: ] haращает оставшуюся часть, незаbkbfhhl
длины строки.
Строки против последовательности байт
Байты - это байты; симheu - это абстракция. Неизменяемая
последоZl_evghklv8QLFRGHkbfолоgZauается строкой ( string ).
Неизменяемая последоZl_evghklv чисел -от -0-до -255 назыZ_lky объект
bytes .
>>> by = b'abcd \x65' ①
>>> by
b'abcde'
>>> type (by ) ②

>>> len (by ) ③
5
>>> by + = b'\xff' ④
>>> by
b'abcde \xff'
>>> len (by ) ⑤
6
>>> by [0] ⑥
97
>>> by [0] = 102 ⑦
Traceback (most recent call last ):
File "" , line 1, in
TypeError : 'bytes' object does not support item assignment
 ① Чтобы создать объект используйте синтаксис "байтоuokljhd bytes
b''. Каждый байт [Zclhой строке может быть либо ASCII симhehf
либо закодироZggufr_klgZ^pZl_jbqgufqbkehfhl до (0 - \x00 \xff
255).
 ② Тип байтоhckljhdb - bytes .
 ③ По аналогии со списками и строками, Вы можете определить длину
байтоhckljhdbkihfhsvx\kljh_gghcnmgdpbb len ().
 ④ По аналогии со списками и строками, Вы можете объединять
байтоu_kljhdbkihfhsvxhi_jZlhjZ . Результат будет ноuf +
h[t_dlhfklbihf bytes .
 ⑤ Объединение 5 -байтоh]hbh^gh[Zclhого объекта даст в результате
6-ти байтоuch[t_dl.
 ⑥ По аналогии со списка ми и строками, Вы можете получить конкретный
байт из байтоhckljhdbih_]hbg^_dkmWe_f_glZfbh[uqghckljhdb

96

uklmiZxlkljhdbZwe_f_glZfb[Zclhой строки яeyxlkyp_eu_qbkeZ
Конкретно числа от 0 до 255.
 ⑦ БайтоZykljhdZg_baf_gy_fZy<ug_fh`_l_b~ менять какие -либо
байты в ней. Если у Вас hagbdeZg_h[oh^bfhklvbaf_gblvhl^_evgu_
байты, то Вы можете либо использоZlvhi_jZlhjdhgdZl_gZpbb( ), +
который дейстm_llZd`_dZdbkhkljhdZfbeb[hdhgертировать объект
bytes h[t_dl bytearray .

>>> by = b'abcd \x65'
>>> barr = bytearray (by ) ①
>>> barr
bytearray (b'abcde' )
>>> len (barr ) ②
5
>>> barr [0] = 102 ③
>>> barr
bytearray (b'fbcde' )
 ① Для конвертироZgbyh[t_dlZ bytes в изменяемый объект bytearray
используйте kljh_ggmxnmgdpbx bytearray ().
 ② Все методы и операторы, которые Вы использоZebkh[t_dlZfblbiZ
bytes , также подходят к объектам bytearray .
 ③ Единст_ggh_hlebqb_khklhbl\lhfqlh<ufh`_l_baf_gblv
значение отдельного байта при работе с объектом bytearray .
ЗаписыZ_fh| значение должно быть целым числом от 0 до 255.
Единст_ggh_q_]h<ug_fh`_l_^_eZlvwlhkf_rb\Zlv[Zclubkljhdb.
>>> by = b'd'
>>> s = 'abcde'
>>> by + s ①
Traceback (most recent call last ):
File "" , line 1, in
TypeError : can 't concat bytes to str
>>> s.count(by) ②
Traceback (most recent call last):
File "", line 1, in
TypeError: Can' t convert 'bytes' object to str implicitly
>>> s. count (by. decode ('ascii' )) ③
1
 ① нельзя соеденить байты и строку. Эта дZjZaguolbiZ^Zgguo.

97

 ② Вы не можете подсчитать частоту klj_qZ_fhklbihke_^hательности
байто\kljhd_ihlhfmqlh\kljhd_ообще нет байтоKljhdZ - это
последоZl_evghklvkbfоло<hafh`gh<ubf__l_\иду "по дсчитать
количестhхождений строки, полученной докодированием
последоZl_evghklb[Zclbadhgdj_lghcdh^bjhки"? Тогда это
необходимо указать точно. Python 3 не будет автоматически
кон_jlbjh\Zlv[Zclu\kljhdbbebkljhdb байты.
 ③ По случайному соiZ^_g ию это строка кода означает "подсчитать
количестhхождений строки, полученной декодированием
последоZl_evghklb[Zclbadhgdj_lghcdh^bjhки".

Здесь пояey_lkykязь между строками и байтами: объект типа bytes имеет
метод decode (), аргументом которого я ey_lkydh^bjh\dZbdhlhjuc
haращает строку. В сhxhq_j_^vkljhdZbf__lf_lh^ encode (), аргументом
которого яey_lkydh^bjhка, и который haращает объект bytes . В
предыдущем примере декодироZgb_[uehhlghkbl_evghijhkluf
последоZl_evghklv[Zcl к одироd_$6&,,ij_h[jZahывалась kljhdmGh
этот процесс подходит для любой кодироdbdhlhjZyih^^_j`bает симheu
строки, даже устареrb_ g| -Unicode) кодироdb.
>>> a_string = '深入 Python' ①
>>> len (a_string )
9
>>> by = a_string. encode ('utf -8' ) ②
>>> by
b'\xe6 \xb7 \xb1 \xe5 \x85 \xa5 Python'
>>> len (by )
13
>>> by = a_string. encode ('gb18030' ) ③
>>> by
b'\xc9 \xee \xc8 \xeb Python'
>>> len (by )
11
>>> by = a_string. encode ('big5' ) ④
>>> by
b'\xb2` \xa4J Python'
>>> len (by )
11
>>> roundtrip = by. decode ('big5' ) ⑤
>>> roundtrip
'深入 Python'
>>> a_string == roundtrip
True

98

 ① Это строки. В ней 9 симheh.
 ② Это объект типа bytes . В нем 13 байт. Это последовательность байт,
полученная кодироZgb_kljhdb a_string dh^bjhке UTF -8.
 ③ Это объект типа bytes . В нем 11 байт. Это последовательность байт,
полученная кодироZgb_kljhdb a_string dh^bjhке GB18030.
 ④ Это объект типа bytes . В нем 11 байт. Это последовательность байт,
полученная кодироZgb_kljhdb a_string dh^bjhке Big5.
 ⑤ Это строка. Она с остоит из девяти симheh. Она предстаey_lba
себя последоZl_evghklvkbfолов, которые Вы получите после
декодироZgby by используя алгоритм кодироdb%LJIhemq_ggZy
строка соiZ^Z_lki_jоначальной.
P.S. Кодировка в исходном коде Python
Python 3 пред полагает, что Zrbkoh^gucdh^ — т.е. каждый файл .py —
записан в кодировке UTF -8.
☞ В Python 2, кодироdhcihmfheqZgbx^eynZceh\S\[ueZdh^bjhка ASCII.
В Python 3 кодироdZihmfheqZgbx — UTF -8.
Если u`_eZ_l_bkihevahать другую кодироdm Zr_fdh^_ы можете
разместить объявление кодироdbgZi_jой строке каждого файла.
Например, для кодироdbZLQGRZs -1252 объяe_gb_ыглядит следующим
образом:
# -*- coding: windows -1252 -*-
Объяe_gb_dh^b роdblZd`_fh`_ljZkiheZ]ZlvkygZторой строке файла,
если перhckljhdhcyляется путь к интерпретатору Python.
#!/usr/bin/python3
# -*- coding: windows -1252 -*-
За дополнительной информацией обращайтесь к PEP 263: Defining Python Source
Code Encodings .
Материалы для дальнейшего чтения
 On Unicode in Python:
 Python Unicode HOWTO
 What’s New In Python 3: Text vs. Data Instead Of Unicode vs. 8 -bit
 PEP 261 explains how Python handles astral characters outside of the Basic
Multilingual Plane (i.e. characters whose ordinal value is greater than 65535)
 On Unicode in general:
 The Absolute Minimum Every Software Developer Absolutely, Positively Must
Know About Unicode and * Character Sets (No Excuses!)
 On the Goodness of Unicode

99

 On Character Strings
 Characters vs. Bytes
 On character encoding in other formats:
 Character encoding in XML
 Character encoding in HTML
 On strings and string formatting:
 string — Common string operations
 Format String Syntax
 Format S pecification Mini -Language
 PEP 3101: Advanced String Formatting

100

Регулярные
ujZ`_gby
❝ Некоторые люди, hремя решения одной проблемы думают: «Я знаю, я
буду использоZlvj_]meyjgu_\ujZ`_gbyL_i_jvmgbo^е проблемы… ❞—
Jamie Zawinski
Погружение
Каждый ноucyaudijh]jZffbjhания имеет kljh_ggu_nmgdpbb^eyjZ[hlu
со строками. В Python, строки имеют методы для поиска и замены: index() ,
find() , split() , count() , replace() и т.д. Но эти методы ограничены для
простейших случаеGZijbf_jf_lh^ index () ищет простую жёстко
заданную часть строки и поиск всегда регистрозаbkbfucQlh[uыполнить
регистронезаbkbfucihbkdihkljhd_ s, вы должны вызZlv s.lower() или
s.upper() для того чтобы быть у_j_ggufqlhkljhdZbf__l
соответстmxsbcj_]bklj^eyihbkd а. Методы replace() и split() имеют те
же ограничения.
Если ZrZaZ^ZqZfh`_l[ulvj_r_gZijbihfhsbwlbof_lh^h\emqr_
использоZlvboHgbijhklu_b[uklju_e_]dhqblZ_fu_fgh]hfh`_l[ulv
сказано о быстром, простом и удобочитаемом коде. Но если uhx наружите
что вы используете большое количестhkljhdh\uonmgdpbckmkeh\byfb if
для обработки специальных случаеbebbkihevam_l_fgh`_klо
последоZl_evguoызоh split() и join() чтобы нарезать на кусочки
ZrbkljhdbagZqblы нуждаетесь j_]meyjguo ujZ`_gbyo.
Регулярные ujZ`_gbywlhfhsgucb ih[hevr_cqZklb
стандартизироZgguckihkh[^eyihbkdZaZf_gubiZjkbg]Zl_dklZijb
помощи комплексных шаблонов. Хотя синтаксис регулярных ujZ`_gbc
довольно сложный и u]ey^blg_ihoh`bfgZghjfZevgucdh^ ijbfi_j
«с махиZ_lgZSHUO dhg_qgucj_amevlZlqZklh более удобочитаемый чем
набор из последоZl_evguonmgdpbc^eykljhdKms_klует даже способ
поместить комментарии внутрь регулярных ujZ`_gbclZdbfh[jZahfы
можете dexqblvg_[hevrmx^hdmf_glZpbx\j_]meyjgh| выражение.

101


Если uihevahались регулярными ujZ`_gbyfb других языках (таких
как Perl, JavaScript, или PHP), синтаксис Python -а будет для вас
достаточно приuqgufIjhqblZcl_h[ahjfh^mey re для того чтобы
узнать о доступных функциях и их аргументах.

Учебный пример: Адрес Улицы
Эта серия примероhkghана на реальных проблемах, которые пояbebkv\
моей работе н есколько лет назад, когда мне пришлось обработать и
стандартизироZlvZ^j_kZmebpwdkihjlbjhанных из устареr_ckbkl_fu^h
того, как произ_klbbfihjl ноmxkbkl_fm H[jZlbl_\gbfZgb_wlhg_
придуманный пример, им всё ещё можно пользоZlvky Wlhlij имер
показыZ_ldZdyih^hrzedijh[e_f_:
>>> s = '100 NORTH MAIN ROAD'
>>> s. replace ('ROAD' , 'RD.' ) ①
'100 NORTH MAIN RD.'
>>> s = '100 NORTH BROAD ROAD'
>>> s. replace ('ROAD' , 'RD.' ) ②
'100 NORTH BRD. RD.'
>>>  UNIQae610d7ca506639d -nowiki -00000003 -QINU  ③
'100 NORTH BROAD RD.'
>>> import re ④
>>> re .sub ('ROAD$' , 'RD.' , s) ⑤
'100 NORTH BROAD RD.'

 ① Моя задача стандартизироZlvZ^j_kmebpugZijbf_j 'ROAD' k_]^Z
ujZ`Z_lkykhdjZs_gb_f 'RD.' . На перucзгляд мне показалось, что
это достаточно просто, и я могу использоZlvf_lh^ replace (). В конце
концов, все данные уже ерхнем регистре и несоiZ^_gb_j_]bkljZg_
состаblijh[e_fuKljhdZihbkdZ 'ROAD' яв лялась константой и
обманчиhijhklhcijbf_j s. replace () _jhylghjZ[hlZ_l.
 ② Жизнь же, напротиihegZijhlbоречиuoijbf_jh, и я быстро
обнаружил один из них. Проблема заключалась в том что 'ROAD'
пояbeZkv\Z^j_k_^ажды, один раз как 'ROAD' , а hт орой как часть
назZgbymebpu 'BROAD' . Метод replace () обнаружиZeхождения и
слепо заменял оба, разрушая таким образом праbevgucZ^j_k.
 ③ Чтобы решить эту проблему вхождения более одной подстроки
'ROAD' , Zfg_h[oh^bfhijb[_]gmlvdke_^mxs_fmbkdZlvbaZf_gylv
'ROAD' ihke_^gboq_lujzokbfолах адреса ( s[-4:]), остаeyykljhdm
отдельно ( s[:-4]). Как ufh]ebaZf_lblvwlhm`_klZghится громоздким.
К примеру, шаблон заbkblhl^ebguaZf_gy_fhckljhdb (Если вы

102

заменяли 'STREET' на 'ST.' , вам придется использоZlv s[:-6] и s[-
6:].replace (... ).) Не хотели бы u\_jgmlvkydwlhfmdh^mq_j_aihe]h^Z
для отладки? Я не хотел бы.
 ④ Пришло j_fyi_j_clb к регулярным ujZ`_gbyf<3\WKRQ\k_
функции, сyaZggu_kj_]meyjgufbыражениями содержится fh^me_
re .
 ⑤ Взглянем на перuciZjZf_lj 'ROAD$' . Это простое регулярное
ujZ`_gb_dhlhjh_gZoh^bl 'ROAD' только в конце строки. Знак $
означает «конец строки ». (Также сущестm_lkbfол ^, означающий
«начало строки».) Используя функцию re .sub () ubs_l_ строке s
регулярное ujZ`_gb_ 'ROAD$' и заменяете на 'RD.' . Оно соiZ^Z_lk
'ROAD' dhgp_kljhdbVgh не соiZ^Z_lk 'ROAD' , являющимся частью
назZgby 'BRO AD' , так как оно находится в середине строки s.
Продолжая историю про обработку адресов, я скоро обнаружил, что
предыдущий пример соiZ^_gby 'ROAD' на конце адреса был недостаточно
хорош, так как не k_Z^j_kZключали k_[yhij_^_e_gb_mebpuG_dhlhju_
адреса просто оканчиZebkvgZaанием улицы. Я избегал этого в большинст_
случаеgh_kebgZaание улицы было 'BROAD' , тогда регулярное ujZ`_gb_
соiZ^Zehk 'ROAD' на конце строки 'BROAD' , чего я со_jr_gghg_ohl_e.
>>> s = '100 BROAD'
>>> re .sub ('ROAD$' , 'RD.' , s)
'100 BRD.'
>>> re .sub ('\\bROAD$' , 'RD.' , s) ①
'100 BROAD'
>>> re .sub (r'\bROAD$' , 'RD.' , s) ②
'100 BROAD'
>>> s = '100 BROAD ROAD APT. 3'
>>> re .sub (r'\bROAD$' , 'RD.' , s) ③
'100 BROAD ROAD APT. 3'
>>> re .sub (r'\bROAD \b', 'RD.' , s) ④
'100 BROAD RD. APT 3'

 ① В дейстbl_evghklb я хотел соiZ^_gbyk 'ROAD' когда оно на
конце строки и является самостоятельным словом (а не частью
большего). Чтобы описать это j_]meyjghfыражении необходимо
использоZlv '\b', что означает «слово должно оказаться прямо тут.» В
Python это сложно, так как '\' знак kljhd_^he`_g[ulvwdjZgbjhан.
Иногда это называют как «бедстb_[wdkewrZbwlhh^gZbaijbqbg
почему регулярные ujZ`_gbyijhs_ Perl чем в Python. Однако
недостаток Perl в том что регулярные ujZ`_gbykf_rb\Zxlkyk^jm]bf

103

синтаксисом, если у Zkhrb[dZ^hklZlhqghkeh`ghhij_^_eblv]^_hgw ,
kbglZdkbk_beb регулярном ujZ`_gbb.
 ② Чтобы обойти проблему «бедствие бэкслэша» вы можете
использоZlvlhqlhgZauается неформатироZggZykljhdw (raw
string) , путём применения префикса строки при помощи симheZ 'r' . Это
скажет Python -у что ничего wlhckljhd_g_^he`gh[ulvwdjZgbjhано;
'\t' это табулятор, но r'\t' это симhe[wdkewrZ '\' , а следом за
ним буква 't' . Я рекомендую k_]^Zbkihevahать неформатироZggmx
строку, когда вы имеете дело с регулярными ujZ`_gbyfbk^jm]hc
стороны всё ста ноblky^hklZlhqghimlZgguf g_kfhljygZlhqlhgZr_
регулярное ujZ`_gbym`_^hklZlhqghaZimlZgh .
 ③ *a^ho* К неудаче я скоро обнаружил больше причин протиhj_qZsbo
моей логике. В этом случае адрес улицы содержал в себе цельное
отдельное слово 'ROAD' и о но не было на конце строки, так как адрес
содержал номер кZjlbjuihke_hij_^_e_gbymebpuLZddZdkeh\h
'ROAD' не находится dhgp_kljhdbj_]meyjgh_ыражение re .sub ()
его пропускало и мы получали на выходе ту же строку что и на oh^_Z
это то чего вы не хотите.
 ④ Чтобы решить эту проблему я удалил симhe '$' и добаbe_szh^bg
'\b'. Теперь регулярное ujZ`_gb_khпадало с 'ROAD' если оно
яeyehkvp_evgufkeh\hf любо й части строки, на конце, в середине и
gZqZe_.

Учебный пример: Римские цифры
Скорее k_]hы видели римские цифры, даже если вы gbog_jZa[bjZ_l_kv
Вы могли b^_lvbogZdhibjZclZoklZjuonbevfh и ТВ -шоу («Copyright
MCMXLVI » f_klh&RS\ULJKW 1946 »), или на стенах [b[ebhl_dZo
уни_jkbl_lh\ mqj_`^_gh MDCCCLXXXVIII » f_klh« учреждено 1888 »). Вы
могли b^_lvbo структуре библиографических ссылок. Эта система
отображения цифр относится к древней Римской империи (отсюда и назZgb_ .
В римских циф рах семь симheh, которые поlhjyxlky\jZaebqguo
комбинациях для отображения цифр.
 I = 1
 V = 5
 X = 10
 L = 50
 C = 100
 D = 500

104

 M = 1000
Нижеследующие праbeZihaоляют конструироZlvjbfkdb_pbnju:
 Иногда симheukdeZ^uаются. I это 1, II это 2, и III это 3. VI это 6
(посимhevgh« 5 и 1»), VII это 7, и VIII это 8.
 Десятичные симheu( I, X, C, и M) могут быть поlhj_gu^hjZa>ey
образоZgby 4 Zfg_h[oh^bfhhlgylvhlke_^mxs_]hысшего симheZ
пятёрки. Нельзя писать 4 как IIII ; f_klhwlh]hhgZaZibkuается как
IV («на 1 меньше 5»). 40 записыZ_lkydZd XL («на 10 меньше 50 »), 41
как XLI , 42 как XLII , 43 как XLIII , и 44 как XLIV («на 10 меньше 50 , и
на 1 меньше 5»).
 Иногда симheuh[jZlgukeh`_gbxJZaf_klb определённые
симh лы до других, uычитаете их от конечного значения. Например 9,
Zfg_h[oh^bfhhlgylvhlke_^mxs_]hысшего символа десять: 8 это
VIII , но 9 это IX («на 1 меньше 10 »), не VIIII (так как симhe I не
может быть поlhjzgjZaZ  90 это XC , 900 это CM .
 Пятёр ки не могут поlhjylvky 10 всегда отображается как X, никогда как
VV . 100 k_]^Z C, никогда LL .
 Римские цифры читаются слева напраhihwlhfmiheh`_gb_kbfола
имеет большое значение. DC это 600 ; CD это со_jr_ggh^jm]ZypbnjZ
(400 , «на 100 меньше 500 »). CI это 101 ; IC это даже не яey_lky
допустимым Римским числом (так как ug_fh`_l_ычитать 1 прямо из
100 ; Zfg_h[oh^bfhaZibkZlvwlhdZd XCIX , «на 10 меньше 100 , и на 1
меньше 10 »).

Проверка на тысячи
Что необходимо сделать чтобы про_jblvqlhijhba\h льная строка яey_lky
допустимым римским числом? Давайте будем брать по одному симhemaZ
один раз. Так как римские числа всегда записыZxlkyhlысшего к низшему,
начнём с ukr_]hklukyqghcihabpbb>eyqbk_ehlbыше,
используются симheu M.
>>> import re
>>> pattern = '^M?M?M?$' ①
>>> re .search (pattern , 'M' ) ②
 UNIQae610d7ca506639d -nowiki -00000039 -QINU 
>>> re .search (pattern , 'MM' ) ③
<_sre.SRE_Match object at 0106C290>
>>> re .search (pattern , 'MMM' ) ④
<_sre.SRE_Match object at 0106AA38>
>>> re .search (pattern , 'MMMM' ) ⑤

105

>>> re .search (pattern , '') ⑥
<_sre.SRE_Match object at 0106F4A8>
 ① Этот патерн состоит из трёх частей. ^ соiZ^Z_lkgZqZehfkljhdb
Если его не указать, патерн будет соiZ^ZlvkF[_amqzlZiheh`_gby\
строке, а это не то что нам надо. Вы должны быть у_j_guqlhkbfолы
М если присутстmxllhgZoh^ylky\gZqZe_kljhdb M? Опционально
соiZ^Z_lkh^gbfkbfол ом M. Так это повторяется три раза то патерн
соiZ^zlhlgmey^hljzojZakkbfолом М в строке. И симhe $
соiZ^zlkdhgphfkljhdbDh]^Zdhf[bgbjm_lkykkbfолом ^ в начале,
это означает что патерн должен совпасть с полной строкой, без других
симheh д о и после симheh\F.
 ② Сущность модуля re это функция search() , которая использует
патерн регулярного ujZ`_gby( pattern ) и строку ( 'M' ) и ищет
соiZ^_gby соот_lklии регулярному ujZ`_gbx?kebkhпадение
обнаружено, search() haращает объект который имеет различные
методы описания соiZ^_gby_kebkhпадения не обнаружено,
search() haращает None , в Python значение нуля ( null ). Всё о чём мы
заботимся в данный момент, соiZ^zlebiZl_jgwlhfh`ghkdZaZlv
глянуgZagZq_gb_озjZsZ_fh_nmgdpb_c sear ch() . 'M' соiZ^Z_lk
этим регулярным ujZ`_gb_flZddZdi_j\h_hipbhgZevgh_ M
соiZ^Z_lZ\lhjh_hipbhgZevgh_Fblj_lv_b]ghjbjm_lky.
 ③ 'MM' соiZ^Z_llZddZdi_jое и lhjh_hipbhgZevgh_Fkhпадает а
третье игнорируется
 ④ 'MMM' соiZ^Z_liheghklvxlZddZdсе три симheZFkhпадают
 ⑤ 'MMMM' не совпадает. Все три М соiZ^Zxlghj_]meyjgh_
ujZ`_gb_gZklZbает на конце строки, (так как требует симhe $), а
строка ещё не кончилась (из за четвёртого М). Поэтому search()
haращае т None .
 ⑥ Занимательно то, что пустая строка также соiZ^Z_lkj_]meyjguf
ujZ`_gb_flZddZdсе симheuFhipbhgZevgu.
Проверка на сотни
? делает патерн необязательным
Расположение сотен более сложное чем тысяч, так как сущестm_lg_kdhevdh
aZbfghbkdexqZxsboiml_caZibkbbaZисит от значения.
 100 = C

106

 200 = CC
 300 = CCC
 400 = CD
 500 = D
 600 = DC
 700 = DCC
 800 = DCCC
 900 = CM
Таким образом есть четыре hafh`guoiZl_jgZ:
 CM
 CD
 От ноль до трёх симheh C (ноль если месть сотен пусто)
 D, от последующх нулей до трёх симheh C
ДZihke_^gboiZl_jgZdhf[bgbjh\Zggu_:
 опциональное D, за ним от нуля до трёх симheh C
Этот пример показыZ_ldZdijhерить позицию сотни jbfkdhfqbke_.
>>> import re
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)$' ①
>>> re .search (pattern , 'MCM' ) ②
 UNIQae610d7ca506639d -nowiki -00000040 -QINU 
>>> re .search (pattern , 'MD' ) ③
 UNIQae610d7ca506639d -nowiki -00000041 -QINU 
>>> re .search (pattern , 'MMMCCC' ) ④
 UNIQae610d7ca506639d -nowiki -00000042 -QINU 
>>> re .search (pattern , 'MCMC' ) ⑤
>>> re .search (pattern , '') ⑥
 UNIQae610d7ca506639d -nowiki -00000043 -QINU 
 ① Этот патерн стартует также как и предыдущий, про_jyykgZqZeZ
строки ( ^), потом тысячи ( M?M?M? ). Следом идёт ноZyqZklv\kdh[dZo
которая описыZ_lljb\aZbfhbkdexqZxsboiZl_jgZjZa^_ezgguo
_jlbdZevghcebgb_c CM , CD и D?C?C?C? (который является
опцион альным D и следующими за ним от нулей до трёх опциональных
симheh C). Парсер регулярного ujZ`_gbyijhеряет каждый из этих
патерно\ihke_^hательности от леh]hdijZому, выбирая перuc
подходящий и игнорируя последующие.

107

 ② 'MCM' соiZ^Z_llZddZdi_j uc M соiZ^Z_l\lhjhcblj_lbckbfол
M игнорируется, симheu CM соiZ^Zxl b CD и D?C?C?C? патерны после
этого не анализируются). MCM это римское предстаe_gb_qbkeZ 1900 .
 ③ 'MD' соiZ^Z_llZddZdi_jый M соiZ^Z_l\lhjhcblj_lbckbfол M
игнорируетс я, и патерн D?C?C?C? СоiZ^Z_lk D (три симheZ C
опциональны и игнорируются). MD это римское предстаe_gb_qbkeZ
1500 .
 ④ 'MMMCCC' соiZ^Z_llZddZdi_j\uc M соiZ^Z_lbiZl_jg D?C?C?C?
сопадает с CCC (симhe D опциональный и игнорируются). MMMCCC i это
римское представление числа 3300 .
 ⑤ 'MCMC' не совпадает. Первый симhe M соiZ^Z_lторой и третий
симhe M игнорируется, также соiZ^Z_l CM , но патерн $ не соiZ^Z_l
так как u_szg_ конце строки (u_szbf__l_g_khпадающий
симhe C). Симhe C не соiZ^Z_ldZdqZklviZl_jgZ D?C?C?C? , так как
исключающий патерн CM уже соiZe.
 ⑥ Занимательно то, что пустая строка kz_szkhпадает с регулярным
ujZ`_gb_flZddZdсе симheuFhipbhgZevgubb]ghjbjmxlkyb
пустая строка совпадает с патерном D?C?C?C? где все симheu
опциональны и игнорируются.
Опаньки! Вы заметили как быстро регулярные ujZ`_gbyklZghятся
отjZlbl_evgufb"BihdZqlhfuh[jZ[hlZeblhevdhihabpbblukyqbkhl_g\
римском представлении чисел. Но если последуете далее, uh[gZjm`bl_qlh
де сятки и единицы описать будет легче, так как они имеют такой же патерн.
Тем j_f_g_f^Z\Zcl_jZkkfhljbf^jm]hcimlvhibkZlvwlhliZl_jg.

Использование синтаксиса {n, m}
модификатор {1,4} совпадает с 1 до 4
вхождением патерна
В предыдущей секции мы имели дело с патерном где одинаковый симhe
может повториться до трёх раз. Сущестm_l^jm]hcimlvaZibkZlvwlh
регулярное ujZ`_gb_dhlhjh_fgh]b_ex^bgZc^ml[he__qblZ_fuf>ey
начала a]eyg_fgZf_lh^dhlhjucfum`_bkihevahали ij_^u^ms_f
примере.

108

>>> import re
>>> pattern = '^M?M?M?$'
>>> re .search (pattern , 'M' ) ①
 UNIQae610d7ca506639d -nowiki -00000045 -QINU 
>>> pattern = '^M?M?M?$'
>>> re .search (pattern , 'MM' ) ②
 UNIQae610d7ca506639d -nowiki -00000046 -QINU 
>>> pattern = '^M?M?M?$'
>>> re .search (pattern , 'MMM' ) ③
 UNIQae610d7ca506639d -nowiki -00000047 -QINU 
>>> re .search (pattern , 'MMMM' ) ④
>>>
 ① Тут патерн совпадает с началом строки и перufhipbhgZevgufFgh
не со lhjufblj_lvbf ghwlhghjfZevgh так как они опциональны), а
также с концом строки.
 ② Тут патерн совпадает с началом строки, с перufbторым
опциональным симhehfFghg_klj_lvbf wlhghjfZevghlZddZdhg
опционален) и с концом строки.
 ③ Тут патерн совпадает с началом строки и со k еми тремя
опциональными симheZfbFblZd`_kdhgphfkljhdb.
 ④ Тут патерн совпадает с началом строки и со k_fblj_fy
опциональными симheZfbFghg_khпадает с концом строки (так как
присутстm_l_szh^ghF lZdbfh[jZahfiZl_jgg_khпадает и
haраща ет None .
>>> pattern = '^M{0,3}$' ①
>>> re .search (pattern , 'M' ) ②
 UNIQae610d7ca506639d -nowiki -00000049 -QINU 
>>> re .search (pattern , 'MM' ) ③
<_sre.SRE_Match object at 0x008EE090>
>>> re .search (pattern , 'MMM' ) ④
<_sre.SRE_Match object at 0x008EEDA8>
>>> re .search (pattern , 'MMMM' ) ⑤
>>>
 ① Этот патерн гоhjblKhпасть с началом строки, потом с от нуля до
трёх симheh М находящимися где угодно, потом с концом строки».
Симheubfh]ml[ulvex[ufbpbnjZfb_kebам необходимо
соiZ^_gb_kb[he__kbfолами М, необходимо записать М{1,3}.

109

 ② Тут патерн совпадает с началом строки, потом с одним из hafh`guo
трёх симheh М, потом с к онцом строки.
 ③ Тут патерн совпадает с началом строки, потом с двумя из hafh`guo
трёх симheh М, потом с концом строки.
 ④ Тут патерн совпадает с началом строки, потом с тремя из hafh`guo
трёх симheh М, потом с концом строки.
 ⑤ Тут патерн совпадает с н ачалом строки, потом с двумя из hafh`guo
трёх симheh М, но не соiZ^Z_l с концом строки.
Регулярное ujZ`_gb_ihaоляет до трёх симheh\F^hdhgpZkljhdbghm
Zkq_luj_biZl_jgозjZsZ_l None .

Проверка на десятки и единицы
Теперь давайте расширим регулярное ujZ`_gb_qlh[uключить десятки и
единицы. Этот пример показыZ_lijhерку на десятки.
>>> pattern = '^M ?M ?M ?( CM |CD |D ?C ?C ?C ?)( XC |XL |L?X?X?X?)$'
>>> re .search (pattern , 'MCMXL ') ①
<_ sre .SRE _Match object at 0x008 EEB 48>
>>> re .search (pattern , 'MCML ') ②
<_ sre .SRE _Match object at 0x008 EEB 48>
>>> re .search (pattern , 'MCMLX ') ③
 UNIQae 610 d7ca 506639 d-nowiki -0000004 F-QINU 
>>> re .search (pattern , 'MCMLXXX ') ④
 UNIQae 610 d7ca 506639 d-nowiki -00000050 -QINU 
>>> re .search (pattern , 'MCMLXXXX ') ⑤
>>>
 ① Тут патерн совпадает с началом строки, потом с перuf
опциональным симhehfFihlhf&0ihlhf;/ihlhfkdhgphfkljhdb
Вспомните что синтаксис (A|B|C) означает «соiZklvlhevdhkh^gbfha
симheh A, B или C» У нас соiZ^Z_l;/bfub]ghjbjm_f;&b
L? X?X?X? , а после этого переходим к концу строки. MCMXL это римское
предстаe_gb_qbkeZ.
 ② Тут патерн совпадает с началом строки, потом с перuf
опциональным симhehfFihlhf&0ihlhfk L?X?X?X?. Из L?X?X?X?
СоiZ^Z_l/bijhimkdZ_lljbhipbhgZevguokbfола X. После этого
переходит к концу строки. MCML это римское предстаe_gb_qbkeZ.

110

 ③ Тут патерн совпадает с началом строки, потом с перuf
опциональным симhehfFihlhf&0ihlhfkhipbhgZevguf/b
перu м опциональным X, пропуская lhjhcblj_lbchipbhgZevgu_
симheu;ihke_wlh]hi_j_oh^blddhgpmkljhdb MCMLX это римское
предстаe_gb_qbkeZ.
 ④ Тут патерн совпадает с началом строки, потом с перuf
опциональным симhehfFihlhf&0ihlhfkhipbhgw льным L и всеми
тремя опциональными симheZfb;ihke_wlh]hi_j_oh^blddhgpm
строки. MCMLXXX это римское предстаe_gb_qbkeZ 1980 .
 ⑤ Тут патерн совпадает с началом строки, потом с перuf
опциональным симhehfFihlhf&0ihlhfkhipbhgZevguf/b\k_fb
тремя опциональными симheZfb;ihke_wlh]h не соiZ^Z_l с концом
строки, так как есть ещё один симhe;lZdbfh[jZahfiZl_jgg_
срабатыZ_lbозjZsZ_l None . MCMLXXXX это недопустимое римское
число.

(A|B) соiZ^Z_leb[hk$eb[hk%.
Для описания единиц подходит тот же патерн. Я уменьшу детализацию и
покажу конечный результат.
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$'
Итак как это будет u]ey^_lvbkihevamyZevl_jgZlbный синтаксис {n,m} ?
Этот пример показыZ_lghый с интаксис.
>>> pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
>>> re .search (pattern , 'MDLV' ) ①
 UNIQae610d7ca506639d -nowiki -00000053 -QINU 
>>> re .search (pattern , 'MMDCLXVI' ) ②
 UNIQae610d7ca506639d -nowiki -00000054 -QINU 
>>> re .search (pattern , 'MMMDCCCLXXXVIII' ) ③
 UNIQae610d7ca506639d -nowiki -00000055 -QINU 
>>> re .search (pattern , 'I') ④
 UNIQae610d7ca506639d -nowiki -00000056 -QINU 
 ① Тут патерн совпадает с началом строки, потом с одним из трёх
hafh`guokbfолов М, потом D?C{0,3} . Из них совпадает только
опциональное D и ни один из опциональных C. Далее соiZ^Z_l
опциональное L из L?X{0,3} и ни один из трёх опциональных X. После
соiZ^Z_lk9ba V?I{0,3} и ни с одним из трёх опциональных I и
наконец с концом строки. MDLV это римск ое предстаe_gb_qbkeZ 1555 .

111

 ② Тут патерн совпадает с началом строки, потом с двумя из трёх
hafh`guokbfолов М, потом D и один опциональный C из D?C{0,3} .
Потом L?X{0,3} с L и один из трёх hafh`guo;ihlhf V?I{0,3} с V и
одним из трёх I, потом с концом строки. MMDCLXVI это римское
предстаe_gb_qbkeZ 2666 .
 ③ Тут патерн совпадает с началом строки, потом с тремя из трёх M,
потом D и C из D?C{0,3}, потом L?X{0,3} с L и три из трёх X, потом
V?I{0,3} с V и тремя из трёх I, потом конец строки. MMMDCCCLXXXVIII
это римское предстаe_gb_qbkeZ 3888 , и это максимально длинное
римское число которое можно записать без расширенного синтаксиса.
 ④ Смотрите gbfZl_evgh Yqmствую себя магом, «Смотрите
gbfZl_evgh^_ldbk_cqZkdjhebd\ue_a_lbafh_creyiu ;)» Тут
соiZ^Z_lgZqZehkljhdbgbh^bgbaljzoFihlhf D?C{0,3} пропускает
опциональный D и три опциональных C, потом L?X{0,3} пропуская
опциональный L и три опциональных X, потом V?I{0,3} пропуская
опциональный V и один из трёх опциональных I. Потом конец с троки.
Стоп, фуф.

Если uke_^hали k_fmbihgyebki_jой попытки, значит у ZkihemqZ_lky
лучше чем у меня. Теперь предстаvl_qlhы пытаетесь разобраться qvbo
то регулярных ujZ`_gbyo Z`ghcnmgdpbb рамках огромной программы.
Или например предс таvl_qlhы возjZsZ_l_kvdkh[kl\_gghcijh]jZff_
через несколько месяцеY^_eZewlhbwlhg_kebrdhfijbylgh_aj_ebs_.
А сейчас давайте исследуем альтернативный синтаксис, который позhebl
легче выполнять поддержку Zrboыражений.

Подробные регулярные выражения
До сих пор ubf_eb^_ehkl_fqlhygZauаю «компактными» регулярными
ujZ`_gbyfbDZdы могли заметить они трудны для прочтения, даже если u
понимаете что они делают. Нет гарантии что ukfh`_l_jZah[jZlvky\gbo
спустя шесть месяц еQlhам дейстbl_evghg_h[oh^bfhlZdwlhложенная
документация
Python позhey_lам сделать это при помощи подробных регулярных
ujZ`_gbc . Подробные регулярные ujZ`_gbyhlebqZxlkyhldhfiZdlguo
дmfykihkh[Zfb:

112

 Пустые строки игнорируются, пробелы, т абы и haраты каретки не
соiZ^Zxlkhhlетст_gghHgbообще не соiZ^Zxl ?keb\uohlbl_
соiZ^_gbykijh[_ehf подробном регулярном ujZ`_gbb\Zf
необходимо постаblv[wdkewri_j_^gbf)
 Комментарии игнорируются. Комментарий в подробном регулярном
выражении такой же как и комментарий dh^_3\WKRQhggZqbgZ_lkyk
симheZb^_cklует до конца строки. В этом случае этото комментарий
это комментарий gmljbfgh]hkljhqghckljhdbghhgjZ[hlZ_llZd`_dZd
и простой.
Пример сделает это более понятным. Да Zcl_i_j_ijhерим компактное
регулярное ujZ`_gb_kdhlhjuffujZ[hlZebbkha^Z^bfih^jh[gh_
регулярное ujZ`_gb_Wlhlijbf_jihdZaZggb`_.
>>> pattern = '''
^ # начало строки
M{0,3} # тысячи - 0 до 3 M
(CM|CD|D?C{0,3}) # сотни — 900 (CM), 400 (CD), 0 -300 (0 до 3 C),
# или 500 -800 (D, с последующими от 0 до 3 C)
(XC|XL|L?X{0,3}) # десятки - 90 (XC), 40 (XL), 0 -30 (0 до 3 X),
# или 50 -80 (L, с последующими от 0 до 3 X)
(IX|IV|V?I{0,3}) # единицы - 9 (IX), 4 (IV), 0 -3 (0 до 3 I),
# или 5 -8 (V, с последующими от 0 до 3 I)
$ # конец строки
'''
>>> re .search (pattern , 'M' , re .VERBOSE ) ①
 UNIQae610d7ca506639d -nowiki -00000058 -QINU 
>>> re .search (pattern , 'MCMLXXXIX' , re .VERBOSE ) ②
 UNIQae610d7ca506639d -nowiki -00000059 -QINU 
>>> re .search (pattern , 'MMMDCCCLXXXVIII' , re .VERBOSE ) ③
<_sre.SRE_Match object at 0x008EEB48>
>>> re .search (pattern , 'M' ) ④
 ① Главное что надо запомнить, это то что необходимо добаeylvwdkljZ
аргументы для работы с ними: re.VERBOSE это константа определённая
fh^me_ re которая служит сигналом что патерн должен быть
использоZgdZdih^jh[gh_j_]meyjgh_\ujZ`_gb_DZdы можете
b^_lvwlhliZl_jgkh^_j`bl[hevrh_dhebq_klо пустых строк. (и все
они игнорируются), а также несколько коммен тарие dhlhju_
игнорируются также). Если мы игнорируем комментарии и пустые строки,
то получается то же самое регулярное выражение что и в предыдущем
примере, но в гораздо более читабельном b^_.
 ② Здесь соiZ^Z_lgZqZehkljhdbihlhfh^ghbljzoозможны х M,
потом CM , потом L и три из hafh`guo X, потом IX , потом конец строки.

113

 ③ Здесь соiZ^Z_lgZqZehkljhdbihlhfljbbaljzoозможных M,
потом D и три из возможных трёх C, потом L и три из трёх hafh`guo X,
потом V и три из трёх hafh`guo I, потом конец строки.
 ④ Тут не соiZ^Z_lIhq_fm"LZddZdhlkmlklует флаг re.VERBOSE и
функция re.search рассматриZ_liZl_jgdZddhfiZdlgh_j_]meyjgh_
ujZ`_gb_k значащими пробелами и симheZfb3\WKRQg_fh`_l
аlhfZlbq_kdbhij_^_eblvyляется ли регулярное выражение
подробным или нет. Python рассматриZ_ldZ`^h_j_]meyjgh_
ujZ`_gb_dZddhfiZdlgh_^hl_oihjihdZ\ug_mdZ`_l_qlhhgh
подробное.

Учебный прим ер: Обработка телефонных номеров
\ d совпадает с любыми цифрами (0 – 9).
\ D совпадает со всем кроме цифр

До сих пор u[uebkdhgp_gljbjhаны на полных патернах. СоiZ^Z_liZl_jg
или не соiZ^Z_lghj_]meyjgu_ыражения могут быть гораздо мощнее
этого. Когда регулярное ujZ`_gb_khпадает с чем либо, ufh`_l_ihemqblv
специально u^_e_ggmxqZklvkhпадения. Вы можете узнать что соiZehb
где.
Эт от пример появился из ещё одной реальной проблемы которые я испытыZe
на предыдущей работе. Проблема была h[jZ[hld_Zf_jbdZgkdbo
телефонных номероDeb_glohq_l_klbl_e_nhggucghf_j простое поле
(без разделителей), но потом также хочет сохранить ин декс, магистраль,
номер и опционально добаhqgmxbgnhjfZpbx базе данных компании. Я
поискал по интернет и нашёл много примероj_]meyjgh]hыражения которое
должно делать это, но к сожалению ни одно из решений не подошло.
Вот телефонные номера которые я должен был обработать:
 800 -555 -1212
 800 555 1212
 800.555.1212
 (800) 555 -1212
 1-800 -555 -1212
 800 -555 -1212 -1234
 800 -555 -1212x1234
 800 -555 -1212 ext. 1234

114

 work 1 -(800) 555.1212 #1234
Достаточно ZjbZglh\ любом из этих примероFg_g_h[oh^bfh[uehagZlv
код 800 , магистраль 555 и остаток номера 1212 . Для тех что с расширениями,
мне необходимо было знать что расширение 1234
Давайте займёмся разработкой решения для обработки телефонного номера.
Этот пример показыZ_l перuc шаг :

>>> phonePattern = re .compile (r'^( \d{3}) -(\d{3}) -(\d{4})$' ) ①
>>> phonePattern. search ('800 -555 -1212' ).groups () ②
('800' , '555' , '1212' )
>>> phonePattern. search ('800 -555 -1212 -1234' ) ③
>>> phonePattern. search ('800 -555 -1212 -1234' ).groups () ④
Traceback (most recent call last ):
File "", line 1, in
AttributeError : 'NoneType' object has no attribute 'groups'

 ① Всегда читайте регулярное ujZ`_gb_ke_\ZgZijZо. Выражение
соiZ^Z_lkgZqZehfkljhdbbihlhfk (\d{3}) . Что такое \d{3} ? Итак ,
\d значит «любая цифра» (от 0 до 9). {3} значит «соiZ^_gb_k
конкретно тремя цифрами»; это ZjbZpbbgZl_fm {n, m} синтаксиса
который вы наблюдали ранее. Если заключить это ujZ`_gb_\djm]eu_
скобки, то это значит «соiZklv^he`ghlhqghljbpbnjubihlhf
запомнить их как группу которую я запрошу позже ». Потом ujZ`_gb_
должно соiZklvk^_nbkhfIhlhfkhпасть с другой группой из трёх
цифр, потм опять дефис. Потом ещё одна группа из четырёх цифр. И в
конце соiZ^_gb_kdhgphfkljhdb.
 ② Чтобы получить доступ к группам которые запомнил обработчик
регулярного ujZ`_gbybkihevamcl_f_lh^ groups() на объекте
который возjZsZ_lf_lh^ search() . Он должен _jgmlvdhjl_`lZdh]h
коли честZ]jmiidhlhjh_[uehhij_^_e_gh\j_]meyjghfыражении. В
нашем случае определены три группы, одна с тремя цифрами, другая с
тремя цифрами и третья с четырьмя цифрами.
 ③ Это регулярное ujZ`_gb_g_hdhgqZl_evguchlет, так как оно не
обрабатыZ_ljZk ширение после телефонного номера. Для этого u
должны расширить регулярное ujZ`_gb_.
 ④ Вот почему ug_^he`gubkihevahать «цепочку» из методо
search() и groups() ijh^Zdrgdh^_?kebf_lh^ search() не _jgzl
соiZ^_gbylhhgернёт None , это не стандартный объект регулярного

115

ujZ`_gby<uah\ None.groups() генерирует очеb^gh_bkdexq_gb_
None не имеет метода groups() . (Конечно же это немного менее
очеb^ghdh]^Z\uihemqZ_l_w то исключение из глубин Zr_]hdh^Z>Z
сейчас это говорит мой опыт.)

>>> phonePattern = re .compile (r'^( \d{3}) -(\d{3}) -(\d{4}) -(\d+)$' ) ①
>>> phonePattern. search ('800 -555 -1212 -1234' ).groups () ②
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800 555 1212 1234' ) ③
>>>
>>> phonePattern. search ('800 -555 -1212' ) ④
>>>
 ① Это регулярное ujZ`_gb_ihqlbb^_glbqghij_^u^ms_fmLZd`_dZd
и до этого оно совпадает с началом строки, потом с запомненной группой
из трёх цифр, потом дефис, потом запомненная группа из трёх цифр,
потом дефис, потом запомненная группа из четырёх цифр . Что же
ноh]h"Wlhkhпадение с другим дефисом и запоминаемая группа из
одной и более цифры.
 ② Метод groups() теперь haращает кортеж из четырёх элементов, а
регулярное ujZ`_gb_l_i_jvaZihfbgZ_lq_luj_]jmiiu.
 ③ К неудаче это регулярное ujZ`_gb_g_y ey_lkynbgZevguf
от_lhflZddZdhghih^jZamf_ает что различные части номера
разделены дефисом. Что случится если они будут разделены пробелами,
запятыми или точками?
 ④ Вам необходимо более общее решение для соiZ^_gbykjZaebqgufb
типами разделителей.
Опаньки! То что делает это регулярное выражение это ещё не соk_flhqlh
uohlbl_<^_cklительности это даже шаг назад, так как ug_fh`_l_
обрабатыZlvl_e_nhggu_ghf_jZ[_ajZkrbj_gbcWlhkhершенно не то что
uohl_eb_kebjZkrbj_gb__klv\u[uo отели знать какое оно, но если его
нет, вы до сих пор хотите знать различные части телефонного номера.

Следующий пример показывает как регулярное ujZ`_gb_h[jZ[Zluает
разделители между различными частями телефонного номера.

>>> phonePattern = re .compile (r'^( \d{3}) \D +( \d{3}) \D +( \d{4}) \D +( \d+)$' ) ①
>>> phonePattern. search ('800 555 1212 1234' ).groups () ②

116

('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800 -555 -1212 -1234' ).groups () ③
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('80055512121234' ) ④
>>>
>>> phonePattern. search ('800 -555 -1212' ) ⑤
>>>

 ① Держите свою шляпу. У Zkkhпадает начало строки, потом группа из
трёх цифр, потом \D+. Что это за чертоsbgZ"Hd , \D соiZ^Z_lkex[uf
симhehfdjhf_pbnjblZd`_hagZqZ_lbeb[he__BlZd \D+
означает один или более симhehfg_yляющихся цифрами. Это то что
ubkihevam_l_\f_klhkbfола дефиса « -» чтобы соiZ^Zehkex[ufb
разделителями.
 ② ИспользоZgb_ \D+ f_klh« -» значит, что теперь регулярное
ujZ`_gb_khпадает с телефонным номером разделённым пробелами
f_klh^_nbkh\.
 ③ Конечно телефонные номера разделенные дефисами тоже
срабатыZxl.
 ④ К неудаче это ещё не окончательный от_llZddZdhgih^jZamf_ает
наличие разделителя. Что если номер едён без kydbojZa^_ebl_e_c?
 ⑤ ОпцаЁ! И до сих пор не решена проблема расширения. Теперь у Zk
д_ijh[e_fughы можете спраblkykgbfbbkihevamylm`_l_ogbdm.

Следующий пример показывает регулярное ujZ`_gb_^ey обработки
телефонных номеро[_ajZa^_ebl_e_c.

>>> phonePattern = re .compile (r'^( \d{3}) \D *( \d{3}) \D *( \d{4}) \D *( \d*)$' ) ①
>>> phonePattern. search ('80055512121234' ).groups () ②
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800.555.1212 x1234' ).groups () ③
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800 -555 -1212' ).groups () ④
('800' , '555' , '1212' , '')
>>> phonePattern. search ('(800)5551212 x1234' ) ⑤
>>>

117

 ① Только одно изменение, замена «+» на «*». Вместо \D+ между
частями номера, теперь используется \D*. Помните что «+» означает «1
или более»? Ок, «*» означает «ноль или более». Итак теперь ufh`_l_
обработать номер даже если он не содержит разделителей.
 ② Подумать только, это дейстbl_evghjZ[hlZ_lIhq_fm"Mас
соiZ^Z_lgZqZehkljhdbihlhfaZihfbgZ_lky]jmiiZbaljzopbnj  
потом ноль или более нецифроuokbfолоihlhfaZihfbgZ_lky
группа из трёх цифр (555), потом ноль или более нецифроuokbfо лов,
потом запоминается группа из четырёх цифр (1212), потом ноль или
более нецифроuokbfолоihlhfaZihfbgZ_lky]jmiiZba
произhevgh]hdhebq_klа цифр (1234), потом конец строки.
 ③ Различные ZjbZpbblZd`_jZ[hlZxllhqdbместо дефисоblZd`_
пробе лы или «x» перед расширением.
 ④ Наконец uj_rbeb^Z\gxxijh[e_fmjZkrbj_gb_kghа
опционально. Если не найдено расширения метод groups() всё ещё
haращает четыре элемента, но четzjlucwe_f_glijhklhimklZy
строка.
 ⑤ Я ненаb`m[ulvестником плохих нов остей, но вы ещё не
закончили. Что же тут за проблема? Сущестmxl^hihegbl_evgu_
симheu^hDUHDdh^Zghj_]meyjgh_\ujZ`_gb_^mfZ_lqlhdh^
города это перh_qlhgZoh^blky начале строки. Нет проблем, u
можете использовать ту же технику «ноль или бол ее нецифроuo
симheh» чтобы пропустить начальные симheu^hdh^Z]hjh^Z.

Следующий пример показывает как работать с симheZfb^hl_e_nhggh]h
номера.

>>> phonePattern = re .compile (r'^\D *( \d{3}) \D *( \d{3}) \D *( \d{4}) \D *( \d*)$' ) ①
>>> phonePattern. search ('(800)5551212 ext. 1234' ).groups () ②
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800 -555 -1212' ).groups () ③
('800' , '555' , '1212' , '')
>>> phonePattern. search ('work 1 -(800) 555.1212 #1234' ) ④
>>>

 ① Это то же самое что и ij_^u^ms_fijbf_j_djhf_ \D*, ноль или
более нецифроuokbfоло^hi_jой запомненной группы (код
города). Заметьте что вы не запоминаете те нецифровые симheu^h

118

кода города (они не kdh[dZo  Если uh[gZjm`bl_boы просто
пропустите их и запомните код города.
 ② Вы можете успешно обработать телефонный номер, даже со скобками
до кода города. (ПраZykdh[dZlZd`_h[jZ[Zluается; как нецифроhc
симhebkhпадает с \D* после перhcaZihfbgZ_fhc группы.)
 ③ Простая про_jdZg_ihehfZebebfuq_]h -то, что должно было
работать. Так как лидирующие символы полностью опциональны,
соiZ^Z_lgZqZehkljhdbghevg_pbnjhых симheh, потом
запоминается группа из трёх цифр (800), потом один нецифроhckbfол
(дефис), потом группа из трёх цифр (555), потом один нецифроhc
(дефис), потом запоминается группа из четырёх цифр (1212), потом ноль
нецифроuokbf\heh\ihlhf]jmiiZpbnjbagmeykbfолоihlhf
конец строки.
 ④ Вот где регулярное выражение выколупыZ_l мне глаза тупым
предметом. Почему этот номер не соiZe"IhlhfmqlhgZoh^blky^h
кода города, но вы допускали что все лидирующие симheu^hdh^Z
города не цифры ( \D*).
Давайте _jgzfkygZaZ^gZk_dmg^m>hkboihjj_]meyjgh_\ujZ`_gb_
соiZ^ZehkgZqZehf строки. Но сейчас вы b^bl_qlh начале могут быть
непредсказуемые симheudhlhju_fuohl_eb[uijhb]ghjbjhать. Лучше не
пытаться подобрать соiZ^_gb_^eygboZijhklhijhimklblvboсе, даZcl_
сделаем другое допущение: не пытаться соiZ^ZlvkgZqZehf строки hh[s_
Этот подход показан в следующем примере.

>>> phonePattern = re .compile (r'(\d{3}) \D *( \d{3}) \D *( \d{4}) \D *( \d*)$' ) ①
>>> phonePattern. search ('work 1 -(800) 555.1212 #1234' ).groups () ②
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800 -555 -1212' ) ③
('800' , '555' , '1212' , '')
>>> phonePattern. search ('80055512121234' ) ④
('800' , '555' , '1212' , '1234' )

 ① Заметьте отсутстb_ в регулярном ujZ`_gbb<u[hevr_g_ ^
khiZ^Z_l_kgZqZehfkljhdbGbq_]hl_i_jvg_ih^kdZauZ_ldZd
ke_^m_lihklmiZlvk_^zggufb^Zggufb\Zr_fmj_]meyjghfm
ujZ`_gbxH[jZ[hlqbdj_]meyjgh]hujZ`_gby[m^_luihegylv
ly`_emxjZ[hlmqlh[ujZah[jZlvky]^_ `__^zggZykljhdZgZqgzl
khiZ^Zlv
 ② Теперь ufh`_l_mki_rghh[jZ[hlZlvl_e_nhggucghf_jdhlhjuc
dexqZ_leb^bjmxsb_kbfолы и цифры, плюс разделители любого типа
между частями номера.

119

 ③ Простая про_jdZ<kzjZ[hlZ_l.
 ④ И даже это работает тоже.
Видит е как быстро регулярное ujZ`_gb_ыходит из под контроля? Бросим
a]ey^gZij_^u^msb_bl_jZpbbFh`_l_ebы объяснить разницу между
одним и другим?
Пока uihgbfZ_l_nbgZevguchlет (а это дейстbl_evghhg_kebы
обнаружили ситуацию которую он не обраб атыZ_lyg_`_eZxh[wlhf
знать), напишем подробное регулярное выражение, до тех пор пока ug_
забыли почему uk^_eZebыбор который uk^_eZeb.

>>> phonePattern = re .compile (r'''
# don't match beginning of string, number can start anywhere
(\d{3}) # area code is 3 digits (e.g. '800')
\D * # optional separator is any number of non -digits
(\d{3}) # trunk is 3 digits (e.g. '555')
\D * # optional separator
(\d{4}) # rest of number is 4 digits (e.g. '1212')
\D * # optional separator
(\d*) # extension is optional and can be any number of digits
$ # end of string
''', re .VERBOSE )
>>> phonePattern. search ('work 1 -(800) 555.1212 #1234' ).groups () ①
('800' , '555' , '1212' , '1234' )
>>> phonePattern. search ('800 -555 -1212' ) ②
('800' , '555' , '1212' , '')

 ① Кроме того что оно разбито на множество строк, это со_jr_ggh
такое же регулярное ujZ`_gb_dZd[ueh\ihke_^g_frZ]_bg_[m^_l
сюрпризом что оно обрабатыZ_llZdb_`_ходные данные.
 ② Финальная простая про_jdZ>Zсё ещё работает. Вы сделали это.
И тоги
Это всего лишь _jomrdZZck[_j]Zlh]hqlhfh]ml^_eZlvj_]meyjgu_
ujZ`_gby>jm]bfbkeh\Zfb^Z`__kebы полностью ошеломлены ими
сейчас, по_jvl_fg_ы ещё ничего не b^_eb.
Вы должны быть сейчас умелыми ke_^mxs_cl_ogbd_:
^ соiZ^_gb_k началом строки.
$ соiZ^_gb_kdhgphfkljhdb.

120

\b соiZ^Z_lk]jZgbp_ckeh\Z.
\d соiZ^Z_lkpbnjhc.
\D соiZ^Z_lkg_pbnjhc.
x? соiZ^Z_lkhipbhgZevgufkbfолом x (другими словами ноль или один
симheh x).
x* соiZ^Z_lkghevbeb[he__[.
x+ соiZ^Z_lk один или более x.
x{n, m} соiZ^Z_lk[g_f_g__QjZaghg_[he__PjZa.
(a|b|c) соiZ^Z_lkDbebEbebF.
(x) группа для запоминания. Вы можете получить значение используя метод
groups() на объекте который haращает re.search.


Регулярные ujZ`_gbywdklj_fZevghfhsgucbgkljmf_glghhgbg_сегда
корректный способ для решения любой проблемы. Вы должны изучить
побольше о них чтобы разобраться когда они яeyxlkyih^oh^ysbfb^ey
решения проблемы, так как иногда они могут добаblv[h льше проблем чем
решить

121

Замыкания и
генераторы

Погружение
По причинам, преhkoh^ysbfсяческое понимание, я k_]^Z\hkobsZeky
языками. Не языками программироZgbyOhly^ZbfbZlZd`_yaudZfb
естест_ggufb<havf_fdijbf_jmZg]ebckdbc:g]ebckdbcyaud — это
шизофренический язык, который заимстm_lkeh\Zbag емецкого,
французского, испанского и латинского языко g_]h\hjym`_h[hklZevguo 
Откровенно гоhjyaZbfklует» — неуместное слово; он их скорее «hjm_l
Или, hafh`ghZkkbfbebjm_l» — как Борги . Да, хороший ZjbZgl.
«
Мы Борги . Ваши лингbklbq_kdb| =и этимологические особенности
станут нашими. Сопротиe_gb_[_kihe_agh. = »
В этой гла_ы узнаете о сущестbl_evguoо множест_gghfqbke_LZd`_
umagZ_l_hnmgdpbyodhlhju_озjZsZxl^jm]b_nmgdpbbhkeh`guo
регулярных ujZ`_gbyob]_g_jZlhjZoGhkgZqZeZ^Zайте погоhjbfhlhf
как образуются сущестbl_evgu_\hfgh`_klе нном числе. (Если ug_
читали раздел посys_ggucj_]meyjgufыражениям, сейчас — самое время.
Материал этого раздела подразумевает, что вы понимаете осноuj_]meyjguo
ujZ`_gbcb^h\hevgh[ukljhi_j_c^_l_dbog_ljbиальному
использоZgbx .
Если uырос ли Zg]ehyauqghckljZg_bebbamqZebZg]ebckdbc
формальной школьной обстаноd_ы, _jhylghagZdhfukhkgh\gufb
праbeZfb:
1. Если слово заканчиZ_lky[md\Zfb S, X или Z, следует добаblv ES .
Bass станоblky basses , fax станоblky faxes , а waltz — waltzes .
2. Если слово заканчиZ_lkyaонкой H, следует добаblv ES ; если
заканчивается глухой H , то нужно просто добаblv S. Что такое звонкая
H ? Это такая, которая f_kl_k^jm]bfb[md\Zfbh[t_^bgy_lky зmd

122

который вы можете расслышать. Соот_lklенно, coach ст аноblky
coaches , и rash станоblky rashes , потому что ukeurbl_aуки CH и SH ,
когда произносите эти слова. Но cheetah станоblky cheetahs , потому что
H здесь глухая.
3. Если слово заканчиZ_lkygZ Y, которая читается как I, то замените Y на
IES ; если Y объединена с гласной и зmqbldZd -то по -другому, то просто
добаvl_ S. Так что vacancy станоblky vacancies , но day станоblky
days .
4. Если ни одно из праbeg_ih^oh^blijhklh^h[Zьте S и надейтесь на
лучшее.
(Я знаю, сущестm_lfgh`_klо исключений. Man станоblky men , а woman —
women , но human станоblky humans . Mouse — mice , а louse — lice , но house
hfgh`_kl\_gghcqbke| — houses . Knife станоblky knives , а wife станоblky
wives , но lowlife станоblky lowlifes . И не застаeycl_f_gyглядываться 
слова, которые не изменяются hfgh`_kl\_gghfqbke_dZdgZijbf_j sheep ,
deer или haiku ).
Остальные языки, конечно, со_jr_ggh^jm]b_.
Давайте разработаем библиотеку на Python, которая аlhfZlbq_kdbh[jZam_l
множест_gg ое число английского слова. Мы начнем с этих четырех праbe
но не забывайте, что вам неизбежно понадобится добаeylv_s_.
Я знаю, используем регулярные выражения!
Итак, ukfhljbl_gZkeh\ZbihdjZcg_cf_j_ английском, это означает,
что вы смотрите н а последоZl_evghklbkbfолов. У Zk_klvijZила,
которые гоhjylqlhgm`ghbkdZlvjZagu_dhf[bgZpbbkbfолов, потом
со_jrZlvkgbfbjZaebqgu_^_ckl\byIhoh`_wlhjZ[hlZ^eyj_]meyjguo
ujZ`_gbc!
import re

def plural (noun ):
if re .search ('[sxz]$' , noun ):  UNIQd2f4acf57a9a5326 -ref -00000003 -
QINU 
return re .sub ('$' , 'es' , noun )  UNIQd2f4acf57a9a5326 -ref -00000006 -
QINU 
elif re .search ('[^aeioudgkprt]h$' , noun ):
return re .sub ('$' , 'es' , noun )
elif re .search ('[^aeiou]y$' , noun ):
return re .sub ('y$' , 'ies' , noun )
else :
return noun + 's'

123

1. ↑ Это регулярное ujZ`_gb_ghhghbkihevam_lkbglZdkbkdhlhjucы
не b^_eb\]eZе Регулярные Выражения. Квадратные скобки означают
«найти соiZ^_gbyjhно с одним из этих симheh». Поэтому [sxz ]
означает « s или x, или z», но только один из них. Символ $ должен быть
знаком ZfHgbs_lkhпадения с концом строки. Все регулярное
ujZ`_gb_ijhеряет, заканчиZ_lkyeb noun на s, x или z.
2. ↑ Упомянутая функция re .sub () произh^blaZf| ну подстроки на основе
регулярного ujZ`_gby.
Рассмотрим замену с помощью регулярного ujZ`_gbyнимательнее.
>>> import re
>>> re .search ('[abc]' , 'Mark' ) ①
<_sre. SRE_Match object at 0x001C1FA8 >
>>> re .sub ('[abc]' , 'o' , 'Mark' ) ②
'Mork'
>>> re .sub ('[abc]' , 'o' , 'rock' ) ③
'rook'
>>> re .sub ('[abc]' , 'o' , 'caps' ) ④
'oops'
1. Содержит ли строка Mark симheu a, b или c? Да, содержит a.
2. Отлично, теперь ищем a, b или c и заменяем на o. Mark станоblky Mork .
3. Та же функция преjZsZ_l rock  rook .
4. Вы могли подумать, что этот код caps преобразует в oaps , но он этого не
делает. re .sub заменяет все соiZ^_gbyZg_lhevdhi_jое найденное.
Так что, данное регулярное ujZ`_gb_ij_ратит caps  oops , потому
что оба симheZ c и a заменяются на o.
Вернемся сноZdnmgdpbb plural ()…
def plural (noun ):
if re .search ('[sxz]$' , noun ):
return re .sub ('$' , 'es' , noun ) ①
elif re .search ('[^aeioudgkprt]h$' , noun ): ②
return re .sub ('$' , 'es' , noun )
elif re .search ('[^aeiou]y$' , noun ): ③
return re .sub ('y$' , 'ies' , noun )
else :
return noun + 's'
1. Здесь uaZf_gy_l_dhg_pkljhdb gZc^_gguckihfhsvxkbfола $) на
строку es . Другими словами, добаey_l_ es к строке. Вы могли бы
со_jrblvlh`_kZfh_kihfhsvxdhgdZl_gZpbbkljhdgZijbf_jdZd

124

noun + 'es' , но я предпочел использоZlvj_]meyjgu_\ujZ`_gby^ey
каждого праbeZihijbqbgZfdhlhju_klZgmlykguiha`_.
2. Взгляните -ка, это регулярное ujZ`_gb_kh^_j} ит кое -что ноh_
Симhe ^ dZq_klе перh]hkbfола dадратных скобках имеет
особый смысл: отрицание. [^abc ] означает «любой отдельный симhe
кроме a, b или c». Так что [^aeioudgkprt ] означает любой симhedjhf_ a,
e, i, o, u, d, g, k, p, r или t. Зате м за этим симhehf^he`_g[ulvkbfол h,
следом за ним — конец строки. Вы ищете слова, заканчиZxsb_kygZ H ,
которую можно услышать.
3. То же самое здесь: найти слова, которые заканчиZxlkygZ Y, dhlhjuo
симhei_j_^ Y — не a, e, i, o или u. Вы ищете слова, заканчивающиеся
на Y, которая зmqbldZd I.
Давайте внимательнее рассмотрим регулярные ujZ`_gbykmqZklb_f
отрицания.
>>> import re
>>> re .search ('[^aeiou]y$' , 'vacancy' ) ①
<_sre. SRE_Match object at 0x001C1FA8 >
>>> re .search ('[^aeiou]y$' , 'boy' ) ②
>>>
>>> re .search ('[^aeiou]y$' , 'day' )
>>>
>>> re .search ('[^aeiou]y$' , 'pita' ) ③
>>>
1. vacancy подходит, потому что оно заканчивается на cy , и c — не a, e, i, o
или u.
2. boy не подходит, потому что оно заканчиZ_lkygZ oy , а udhgdj_lgh
указали, что символ перед y не может быть o. day не подходит, потому
что он заканчиZ_lkygZ ay .
3. pita не подходит, потому что оно не заканчивается на y.
>>> re .sub ('y$' , 'ies' , 'vacancy' ) ①
'vacancies'
>>> re .sub ('y$' , 'ies' , 'agency' ) 'agencies'
>>> re .sub ('([^aeiou])y$' , r'\1ies' , 'vacancy' ) ②
'vacancies'
1. Это регулярное выражение преобразует vacancy  vacancies , а agency —
 agencies , что Zfbgm`ghAZf_lvl_qlhhgh[uij_h[jZahало boy 
boies , но этого nmgdpbbgbdh]^Zg_ijhbahc^_lihlhfmqlhы сначала
сделали re .search с целью uykgblvke_^m_leb^_eZlv re .sub .
2. Замечу заодно, что hafh`ghh[t_^bgblvwlb^а регулярных ujZ`_gby
(одно чтобы uykgblvijbf_gy_lkyebijZило, а другое чтобы

125

собст_ggh_]hijbf_gblv  одно регулярное ujZ`_gb_<hllZd
u]ey^_e[uj_amevlZl;hevrZyqZklv^he`gZ[ulv\ZfagZdhfZы
используете запоминаемую группу, о которой umagZebbaMq_[guc
пример: Разбор телефонного номера. Группа используется чтобы
зап омнить симhei_j_^ y. Затем ih^klZghочной строке, u
используете ноuckbglZdkbk \1, который означает «эй, та перZy
группа, которую ты запомнил? положи ее сюда». Таким образом, вы
помните c перед y; когда u^_eZ_l_ih^klZghку, uklZите c на место
c, и ies на место y. (Если у Zk[he__h^ghcaZihfbgZ_fhc]jmiiu
можете использовать \2 и \3 и так далее.)
Замены с использоZgb_fj_]meyjguoыражений яeyxlkyqj_aычайно
мощным инструментом, а синтаксис \1 делает их еще более мощным. Но ky
операция, объединенная в одно регулярное ujZ`_gb_lZd`_klZghится
сложной для чтения, кроме того такой способ не соотносится напрямую с тем,
как ubagZqZevghhibkZebijZила формироZgbyfgh`_kl\_ggh]hqbkeZ
Изначал ьно ukijh_dlbjhали праbeZ форме «если слово заканчивается
на S, X или Z, то добаvl_ ES ». А если ukfhljbl_gZnmgdpbxlhmас —
д_kljhdbdh^Zdhlhju_]hорят «если слово заканчиZ_lkygZ S, X или Z, то
добаvl_ ES ». Еще ближе к оригинальному в арианту приблизиться никак не
получится.
Список функций
Сейчас u^h[Zите уро_gvZ[kljZdpbb<ugZqZebkhij_^_e_gbykibkdZ
праbe_kebерно это, сделай то, иначе обращайтесь к следующему праbem
Давайте j_f_gghmkeh`gbfqZklvijh]jZffulZdqlhы с можете упростить
другую ее часть.
import re

def match_sxz (noun ):
return re .search ('[sxz]$' , noun )

def apply_sxz (noun ):
return re .sub ('$' , 'es' , noun )

def match_h (noun ):
return re .search ('[^aeioudgkprt]h$' , noun )

def apply_h (noun ):
return re .sub ('$' , 'es' , noun )

def match_y (noun ): ①
return re .search ('[^aeiou]y$' , noun )

126


def apply_y (noun ): ②
return re .sub ('y$' , 'ies' , noun )

def match_default (noun ):
return True

def apply_default (noun ):
return noun + 's'

rules = ((match_sxz , apply_sxz ), ③
(match_h , apply_h ),
(match_y , apply_y ),
(match_default , apply_default )
)

def plural (noun ):
for matches_rule , apply_rule in rules: ④
if matches_rule (noun ):
return apply_rule (noun )
1. Теперь каждое праbeh -условие совпадения яey_lkyhl^_evghc
функцией которая haращает результаты uahа функции re .search ().
2. Каждое праbeh -дейстb_lZd`_yляется отдельной функцией, которая
uauает функцию re .sub () чтобы применить соот_lklующее праbeh
формироZgbyfgh`_kl\_ggh]hqbkeZ.
3. Вместо одной функции ( plural ()) с несколькими праbeZfbmас теперь
есть структура данн ых rules , яeyxsZykyihke_^hательностью пар
функций.
4. Поскольку праbeZjZa\_jgmlu\hl^_evghckljmdlmj_^Zgguogh\Zy
функция plural () может быть сокращена до нескольких строк кода.
Используя цикл for , из структуры rules можно изe_qvijZила условия и
за мены одноj_f_gghIjbi_jой итерации for цикла, match_rules
станет match_sxz , а apply_rule станет apply_sxz . Во j_fyторой
итерации, если мы до нее дойдем, matches_rule будет присh_gh
match_h , а apply_rule станет apply_h . Функция гарантироZgghернет
что -нибудь по окончании работы, потому что последнее праbeh
соiZ^_gby( match_default ) просто возjZsZ_l True , подразумеZyqlh
соответстmxs__ijZило замены ( apply_default ) k_]^Z[m^_l
применено.
Причиной, по которой этот пример работает, яey_lkylhlnZdlqlh Python
k_yляется объектом, даже функции. Структура данных rules содержит
функции — не имена функций, а фактические функции -объекты. Когда они

127

присZbаются  for цикле, matches_rule и apply_rule яeyxlkygZklhysbfb
функциями, которые ufh`_l_ызыZlvIjbi_jой итерации for цикла, это
экbалентно uahу matches_sxz (noun ), и если она возjZsZ_lkhпадение,
uahу apply_sxz (noun ).
-> Переменная « rules » — это последоZl_evghklviZjnmgd ций. [wa р-
rob in.с om ]
Если этот дополнительный уро_gvZ[kljZdpbbk[bает Zkklhedm
попробуйте раз_jgmlvnmgdpbxqlh[umидеть, что мы получаем то же
самое. Весь цикл for экbалентен следующему :
def plural (noun ):
if match_sxz (noun ):
return apply_sxz (noun )
if match_h (noun ):
return apply_h (noun )
if match_y (noun ):
return apply_y (noun )
if match_default (noun ):
return apply_default (noun )
Преимущестhfa^_kvyляется то, что функция plural () упрощена. Она
принимает последоZl_evghklvijZил, определенных где -либо, и проходит
по ним.
1. Получить праbehkhпадения
2. Правило срабатыZ_l"Lh]^Zijbf_gblvijZило замены и _jgmlv
результат.
3. Нет соiZ^_gbc"GZqZlvkimgdlZ.
Правила могут быть определ ены где угодно, любым способом. Для функции
plural () абсолютно нет никакой разницы.
Итак, добаe_gb_wlh]hmjhня абстракции стоило того? Вообще -то пока нет.
Попробуем предстаblvqlhihlj_[m_lky^ey^h[Zления нового праbeZ
функцию. В перhfijbf_j_w того потребоZeh[u^h[Zить ноmx
конструкцию if в функцию plural (). Во lhjhfijbf_j_wlhihlj_[hало бы
добаblv^е функции, match_foo () и apply_foo (), а затем обноblv
последоZl_evghklv rules чтобы указать, когда ноu_ijZила соiZ^_gbyb
замены до лжны быть uaаны по отношению к остальным праbeZf.
Но на самом деле это только средстhqlh[ui_j_clbdke_^mxs_c]eZ\_
Дb]Z_fky^Zevr_…

128

Список шаблонов
Определение отдельных именоZgguonmgdpbc^eydZ`^h]hmkeh\byb
праbeZaZf_gu\hсе не яey_lkyg_h[oh^bfhklvx<ugbdh]^Zg_ызыZ_l_
их напрямую; u^h[Zляете их ihke_^hательность rules и uau\Z_l_bo
через нее. Более того, каждая функция следует о дному из дmorZ[ehgh. Все
функции соiZ^_gbyызыZxl re .search (), а все функции замены uauают
re .sub (). Давайте исключим шаблоны, чтобы объяe_gb_gh\uoijZил было
более простым.
import re

def build_match_and_apply_functions (pattern , search , replace ):
def matches_rule (word ): ①
return re .search (pattern , word )
def apply_rule (word ): ②
return re .sub (search , replace , word )
return (matches_rule , apply_rule ) ③
1. build_match_and_apply_functions () — это функция, которая динамически
создает другие функции. Она принимает pattern , search и replace , затем
определяет функцию matches_rule (), которая uauает re .search () с
шаблоном pattern , переданный функции
build_match_and_apply_functions () в качест_Zj]mf_glZb word , который
передан функции matches_rule (), которую uhij_^_ey_l_.
2. Строим функцию apply тем же способом. Функция apply — это функция,
которая принимает один параметр, и uauy ает re .sub () с search и replace
параметрами, переданными функции build_match_and_apply_functions , и
word , переданным функции apply_rule (), которую ukha^Z_l_Ih^oh^
заключающийся bkihevahании значений внешних параметров gmljb
динамической функции на зыZ_lkyaZfudZgbyfbIhkmlbы
определяете константы nmgdpbbaZf_guhgijbgbfZ_lh^bgiZjZf_lj
(word ), но затем дейстm_lbkihevamy_]hb^а других значения ( search и
replace ), которые были устаноe_gu момент определения функции
замены.
3. В конце кон цов функция build_match_and_apply_functions () haращает
кортеж с дmfyagZq_gbyfb^умя функциями, которые ulhevdhqlh
создали. Константы, которые uhij_^_ebebнутри тех функций ( pattern
gmljbnmgdpbb match_rule ()), search и replace в функции apply_rule ())
остаются с этими функциями, даже. Это безумно круто.
Если это сбиZ_lас с толку (и так и должно быть, это _kvfZkljZggh_
по_^_gb_ dZjlbgZfh`_lijhykgblvkydh]^Zы уb^bl_dZdbkihevahать
этот подход.

129

patterns = \ ①
(
('[sxz]$' , '$' , 'es' ),
('[^aeioudgkprt]h$' , '$' , 'es' ),
('(qu|[^aeiou])y$' , 'y$' , 'ies' ),
('$' , '$' , 's' ) ②
)
rules = [build_match_and_apply_functions (pattern , search , replace ) ③
for (pattern , search , replace ) in patterns ]
1. Наши правила формироZgbyfgh`_klенного числа теперь определены
как кортеж кортежей строк (не функций). ПерZykljhdZ каждой
группе — это регулярное ujZ`_gb_dhlhjh_ы бы использоZeb
re .search () чтобы определить, подходит ли данное праbeh<lhjZyb
третья строки dZ`^hc]jmii| — это ujZ`_gby^eyihbkdZbaZf_gu
которые u[ubkihevahали в re .sub () чтобы применить праbehb
преобразоZlvkms_klительное hfgh`_klенное число.
2. В альтернатиgh м праbe__klvg_[hevrh_baf_g_gb_<ijhrehf
примере функция match_default () просто haращает True , подразумеZy
что если ни одно конкретное праbehg_ijbf_gbehkvdh^^he`_gijhklh
добаblv s dhg_p^Zggh]hkeh\ZNmgdpbhgZevgh^Zggucijbf_j^_eZ_l
то же самое. Окончательное регулярное выражение узнает,
заканчивается ли слово ( $ ищет конец строки). Конечно же, у каждой
строки есть конец, даже у пустой, так что ujZ`_gb_\k_]^ZkjZ[Zluает.
Таким образом, она служит той же цели, что и функция match_def ault (),
которая всегда haращала True : она гарантирует, что если нет других
конкретных uiheg_gguoijZил, код добаey_l s в конец данного слова.
3. Это волшебная строка. Она принимает последоZl_evghklvkljhd
patterns и превращает их ihke_^hательность функций. Как?
«Отображением» строк в функцию build_and_apply_functions (). То есть
она берет каждую тройку строк и uauает функцию
build_match_and_apply_functions () с этими тремя строками dZq_kl\_
аргументоNmgdpby build_match_and_apply_functions () h~ jZsZ_l
кортеж из дmonmgdpbcWlhhagZqZ_lqlh rules в конце концо
функционально станоblkywdиZe_glghcij_^u^ms_fmijbf_jmkibkhd
кортежей, где каждый кортеж — это пара функций. ПерZynmgdpby —
это функция соiZ^_gbydhlhjZyызыZ_l re .search (), а вторая
функция — применение праbeZ aZf_gZ dhlhjZyызыZ_l re .sub ().
За_jrbfwlmерсию скрипта глаghclhqdhcхода, функцией plural ().
def plural (noun ):
for matches_rule , apply_rule in rules: ①
if matches_rule (noun ):
return apply_rule (noun )

130

1. Поскольку список rules — тот же самый, что и в предыдущем примере
(да, так и есть), нет ничего удиbl_evgh]h\lhfqlhnmgdpby plural ()
соk_fg_baf_gbeZkvHgZyляется полностью обобщенной; она
принимает список функций -праbeb uau\Z_lboihihjy^dm?_g_
hegm_ldZdhij_^_e_guijZила. В предыдущем примере они были
определены как отдельные именоZggu_nmgdpbbL_i_jv`_hgb
создаются динамически сопостаe_gb_fj_amevlZlZnmgdpbb
build_match_and_apply_functions () списку обычны х строк. Это не играет
никакой роли. функция plural () продолжает работать как и раньше.
Файл шаблонов
Вы ug_kebесь дублирующийся код и добаbeb^hklZlhqghZ[kljZdpbc^ey
hafh`ghklbojZgblvijZила формироZgbyfgh`_klенного числа kibkd_
строк. Следующий логический этап — aylvwlbkljhdbbjZkiheh`blvbo
отдельном файле, где они могут поддержиZlvkyhl^_evghhlbkihevamxs_]h
их кода.
Во -перuo^Z\Zcl_kha^Z^bfl_dklhый файл, содержащий нужные нам
праbeZGbdZdbokeh`guokljmdlmj^Zgguoijh сто разделенные на три
колонки данные. Назо_f_]hSOXUDO4 -rules.txt
[sxz]$ $ es
[^aeioudgkprt]h$ $ es
[^aeiou]y$ y$ ies
$ $ s
Теперь давайте посмотрим, как ufh`_l_bkihevah\ZlvwlhlnZcek
праbe ами.
import re

def build_match_and_apply_functions (pattern , search , replace ): ①
def matches_rule (word ):
return re .search (pattern , word )
def apply_rule (word ):
return re .sub (search , replace , word )
return (matches_rule , apply_rule )

rules = []
with open ('plural4 -rules.txt' , encoding ='utf -8' ) as pattern_file: ②
for line in pattern_file: ③
pattern , search , replace = line. split (None , 3) ④
rules. append (build_match_and_apply_functions ( ⑤
pattern , search , replace ))

131

1. Функция build_match_and_apply_functions () не изменилась . Вы по -
прежнему используете замыкания, чтобы динамически создать д_
функции, которые будут использоZlv переменные из внешней функции.
2. Глобальная функция open () открывает файл и haращает файловый
объект. В данном случае файл, который мы открыZ_fkh^_j`blkljhdb -
шаблоны для праbenhjfbjhания множест_ggh]hqbkeZ
Ут_j`^_gb_ with создает то, что назыZ| тся контекстом: когда блок with
заканчивается, Python аlhfZlbq_kdbaZdjh_lnZce^Z`__kebнутри
блока with было выброшено исключение. Подробнее о блоках with и
файловых объектах umagZ_l_ba]eZ\uNZceu.
3. Форма « for line in » читает данные из открытого файла
построчно и присваиZ_ll_dkli_j_f_gghc line . Подробнее про чтение
файлов umagZ_l_ba]eZы Файлы.
4. Каждая строка nZce_^_cklительно содержит три значения, но они
разделены пустым пространстhf lZ[meypb_cbebijh[_e ами, без
разницы). Чтобы разделить их, используйте строкоucf_lh^ split ().
Первый аргумент для split () — None , что означает «разделить любым
симhehfk\h[h^gh]hijhkljZgklа (табуляцией или пробелом, без
разницы)». Второй аргумент — 3, что означает «разби ть свободным
пространстhfjZaZaZl_fhklZить остальную часть строки» Строка
b^Z« [sxz ]$ $ es » будет разбита и преобразоZgZ список ['[sxz]$' , '$' ,
'es' ], что означает что pattern станет '[sxz]$' , search — '$' , а replace
получит значение 'es' . Это д оhevghfhsgh^eyh^ghcfZe_gvdhckljhdb
кода
5. В конце концов, вы передаете pattern , search и replace функции
build_match_and_apply_function (), которая haращает кортеж функций.
Вы добаey_l_wlhldhjl_` список rules , и в завершении rules хранит
список ф ункций поиска соiZ^_gbcbыполнения замен, который
ожидает функция plural ().
Сделанное улучшение заключается lhfqlhы полностью ug_kebijZила
hнешний файл, так что он может поддержиZlvkyhl^_evghhl
использующего его кода. Код — это код, данные — это данные, а жизнь
хороша.
Генераторы
Но _^v[m^_ldjmlh_kebh[h[s_ggZynmgdpby plural () будет разбирать файл с
праbeZfb"Baлеки праbeZgZc^bkhпадения, примени соот_lklующие
изменения, переходи к следующему праbemWlhсе, что функции plural ()
придется делать, и больше ничего от нее не требуется.
def rules (rules_filename ):
with open (rules_filename , encoding ='utf -8' ) as pattern_file:
for line in pattern_file:
pattern , search , replace = line. split (None , 3)

132

yield build_match_and_apply_functions (pattern , search , replace )

def plural (noun , rules_filename ='plural5 -rules.txt' ):
for matches_rule , apply_rule in rules (rules_filename ):
if matches_rule (noun ):
return apply_rule (noun )
raise ValueError ('no matching rule for {0}' .format (noun ))
Как черт havfbwlhjZ[hlZ_l">Z\Zcl_kgZqZeZihkfhljbfgZijbf_jk
пояснениями.
>>> def make_counter (x):
... print ('entering make_counter' )
... while True :
... yield x ①
... print ('incrementing x' )
... x = x + 1
...
>>> counter = make_counter (2) ②
>>> counter ③

>>> next (counter ) ④
entering make_counter
2
>>> next (counter ) ⑤
incrementing x
3
>>> next (counter ) ⑥
incrementing x
4
1. Присутстb_dexq_ого слова yield  make_counter означает, что это не
обычная функция. Это особый b^nmgdpbbdhlhjZy]_g_jbjm_l
значения по одному. Вы можете думать о ней как о продолжаемой
функции. Её uah _jgzl]_g_jZlhjdhlhjucfh`_l[ulvbkihev зоZg
для генерации последующих значений x.
2. Чтобы создать экземпляр генератора make_counter , просто uahите его
как и любую другую функцию. Заметьте, что фактически это не
uihegy_ldh^Znmgdpbb<ufh`_l_lZdkdZaZlvihlhfmqlhi_jая
строка функции mak e_counter () uauает print (), но ничего до сих пор не
напечатано.
3. Функция make_counter () haращает объект -генератор.
4. Функция next () принимает генератор и haращает его следующее
значение. ПерucjZadh]^Zы uauаете next () с генератором counter ,

133

он исполняет код  make_counter () до перh]hmlерждения yield , затем
haращает значение, которое было haращено yield . В данном случае,
это будет 2, поскольку изначально вы создали генератор uahом
make_counter (2).
5. Поlhjgucызов next () с тем же генер атором продолжает uqbke_gby
точно там, где они были прерваны, и продолжает до тех пор, пока не
klj_lblke_^mxsbc yield . Все переменные, локальные состояния и т. д.
сохраняются hремя yield , и hkklZgZлиZxlkyijbызо_ next ().
Следующая строка кода, ожидающая исполнения, uauает print (),
который печатает incrementing x . После этого следует ут_j`^_gb_ x = x
+ 1. Затем сноZbkihegy_lkypbde while , и перh_qlh нём
klj_qZ_lky — ут_j`^_gb_ yield x, которое сохраняет все состояние и
haращает теку щее значение x (сейчас это 3).
6. После lhjh]hызоZ next (counter ) происходит kzlh`_kZfh_lhevdh
теперь x станоblkyjZ\guf.
Поскольку make_counter oh^bl бесконечный цикл, вы бы теоретически
могли заниматься этим бесконечно, а он продолжал бы у_ebqbать x и
haращать его значения. Но f_klhwlh]h^Z\Zcl_ihkfhljbfgZ[he__
продуктивное использоZgb_]_g_jZlhjh\.
Генератор последовательности Фибо наччи
def fib (max ):
a, b = 0, 1 ①
while a < max :
yield a ②
a, b = b, a + b ③
1. ПоследоZl_evghklvNb[hgZqqb — это последовательность чисел, 
которой каждое число яey_lkykmffhc^ух предыдущих. Она
начинается с 0 и 1, сначала постепенно hajZklZ_lihlhfjZkl_l\k_
быстрее и быстрее. Чтобы начать последоZl_evghklv\Zfg_h[oh^bfh
д_i_j_f_ggu_ а начинает с 0, а b — с 1.
2. a яey_lkygZqZevgucagZq_gb_fihke_^hательности, поэтому её
следует _jgmlv.
3. b яey_lkyke_^mxsbfqbkehfihke_^hательности, так что присhcl_
ее a, но так же посчитайте следующее значение ( a + b ) и присhcl__]h b
для последующего использоZgbyAZf_lvl_qlhwlhijhbkoh^bl
одноj_f_ggh?keb a равно 3, а b раghlh a, b = b, a + b устаноbl a 
5 (предыдущее значение b) а b  kmffZij_^u^msboagZq_gbc a и b).
Так что теперь у вас есть функ ция выдает последоZl_evgu_qbkeZ
Фибоначчи. Конечно, ufh]eb[uk^_eZlvwlhkihfhsvxj_dmjkbbgh^ZggZy
реализация проще читается. Помимо этого, она лучше работает с циклами for .

134

->yield приостанавлиZ_lnmgdpbx next () сноZaZimkdZ_l__\lhf`_
состоянии
>>> from fibonacci import fib
>>> for n in fib (1000 ): ①
... print (n, end =' ') ②
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> list (fib (1000 )) ③
[0, 1, 1, 2, 3, 5, 8, 13 , 21 , 34 , 55 , 89 , 144 , 233 , 377 , 610 , 987 ]
1. Вы можете использоZlv]_g_jZlhjlbiZ fib () непосредст_ggh\ for
цикле. Цикл for автоматически uahuает функцию next () чтобы получить
значения из генератора fib () и присвоить их переменной for цикла n.
2. Каждый раз проходя for цикл, n принимает ноh_agZq_gb_hl yield 
функции fib (), и все что Zfgm`ghk^_eZlv — напечатать его. Как только
fib () uoh^blaZij_^_euqbk_e( a станоblky[hevr_q_f max , которое 
данном случае раgh lZdkjZampbde for заканчиZ_ljZ[hlm.
3. Это полез ная техника: отдайте генератор функции list (), и она пройдет в
цикле _kv]_g_jZlhj lhqghlZd`_dZdbpbde for ij_^u^ms_f
примере) и _jg_lkibkhd\k_oagZq_gbc.
A Plural Rule Generator
Давайте _jg_fkydSOXUDOS\bihkfhljbf как работает эта _jkbynmgdpbb
plural ().
def rules (rules_filename ):
with open (rules_filename , encoding ='utf -8' ) as pattern_file:
for line in pattern_file:
pattern , search , replace = line. split (None , 3) ①
yield build_match_and_apply_functions (pattern , search , replace ) ②

def plural (noun , rules_filename ='plural5 -rules.txt' ):
for matches_rule , apply_rule in rules (rules_filename ): ③
if matches_rule (noun ):
return apply_rule (noun )
raise ValueError ('no matching rule for {0}' .format (noun ))
1. Никакой магии. Помните, что строки файла правил содержат по три
значения, разделенных пустым пространстhflZdqlhы используете
line. split (None , 3) чтобы получить три «колонки» и присhblvbolj_f
локальным переменным.
2. А затем вы uau\Z_l_ yield . Что uозjZsZ_l_">\_nmgdpbb
созданные динамически ZrbfklZjufihfhsgbdhf
build_match_and_apply_functions (), который такой же как и в предыдущих

135

приме рах. Другими словами, rules () — это генератор, который отдаёт
праbeZkhпадения и изменения по требоZgbx.
3. Поскольку rules () — это генератор, ufh`_l_bkihevahать его
напрямую  for цикле. При перhfijhoh`^_gbb for цикла, uызо_l_
функцию rules (), к оторая откроет файл шаблоноijhqblZ_li_jую
строку, динамически построит функцию условия и модификации из
шаблона в этой строке, и _jg_l^bgZfbq_kdbkha^Zggu_nmgdpbbIjb
прохождении через loop цикл lhjhcjZa\uijh^he`bl_jh\ghklh]h
места, в кото ром покинули rules () (это было gmljb for line in pattern_file
цикла). Перh_qlhhgk^_eZ_l — прочитает следующую строку файла
(который до сих пор открыт), динамически создаст другие функции
условия и модификации на осноZgbbrZ[ehgh этой строки файла, и
отдаст эти д_nmgdpbb.
Что uijbh[j_ebihkjZ\g_gbxk\Zjb антом 4? Меньшее j_fyaZimkdZ<
ZjbZgl_dh]^Z\ubfihjlbjhали модуль plural4 , он читал _kvnZce
шаблонов и строил список всех hafh`guoijZил ещё до того, как uообще
подумали о uah\_nmgdpbb plural (). С генераторами вы можете uihegylv\k_
дейс тbye_gbо: uqblZ_l_i_jое праbehbkha^Z_l_nmgdpbbbijh[m_l_
их, и если это срабатыZ_l\ugbdh]^Zg_qblZ_l_hklZevghcnZcebg_
создаете другие функции.
В чем вы теряете? В произh^bl_evghklbDZ`^ucjZadh]^Zы вызыZ_l_
функцию plural (), генер атор rules () начинает всё с начала — что означает
открытие заноhnZceZrZ[ehgh\bql_gb_kkZfh]hgZqZeZihkljhqgh.
Что если бы ufh]ebзять лучшее из дmofbjh: минимальные расходы на
запуск (не исполнять никакого кода hремя import ) и максимальная
произh^bl_evghklv g_kha^Z\Zlvh^gbbl_`_nmgdpbbkghа и сноZ B^Z
uih -прежнему хотите хранить праbeZ отдельном файле (потому что
код — это код, а данные — это данные), настолько долго, пока Zfдруг не
потребуется читать одну и ту же строку дZ`^u.
Чтобы сделать это, Zfg_h[oh^bfh[m^_lihkljhblvkой собст_gguc
итератор. Но перед тем как uk^_eZ_l_wlhам необходимо изучить классы в
Python.
Материалы для дальнейшего чтения
 PEP 255: Simple Generators
 Understanding Python’s «with» statement
 Closures in Python
 Числа Фибоначчи
 English Irregular Plural Nouns

136

Классы и
итераторы

Погружение
Генераторы на самом деле — k_]hebrvqZklguckemqZcbl_jZlhjh\
Функция, haращающая (yields) значения, яey_lkyijhklufbdhfiZdlguf
способом получения функциональности итератора, без непосредст_ggh]h
создания итератора. Помните генератор чисел Фибонач чи? Вот набросок того,
как мог бы выглядеть аналогичный итератор:
class Fib:
'''iterator that yields numbers in the Fibonacci sequence'''

def __init__ (self , max ):
self .max = max

def __iter__ (self ):
self .a = 0
self .b = 1
return self

def __next__ (self ):
fib = self .a
if fib > self .max :
raise StopIteration
self .a, self .b = self .b, self .a + self .b
return fib
Давайте рассмотрим этот пример более детально:
class Fib:
class ? Что такое класс?

137

Определение классов
Python полностью объектно -ориентироZglh_klv\ufh`_l_hij_^_eylvk\hb
собст_ggu_deZkkugZke_^hать ноu_deZkkuhlk\hbobebстроенных
классов, и создавать экземпляры классоdhlhju_m`_hij_^_ebeb.
Опре делить класс 3\WKRQijhklhLZd`_dZdb\kemqZ_knmgdpbyfb
раздельное объяe_gb_bgl_jn_ckZg_lj_[m_lky<uijhklhhij_^_ey_l_
класс и начинаете программироZlvHij_^_e_gb_deZkkZ\3\WKRQgZqbgZ_lky
с зарезерbjhанного слоZ class , за которым след ует имя (идентификатор)
класса. Формально, это все, что необходимо, kemqZ_dh]^ZdeZkkg_^he`_g
быть унаследоZghl^jm]h]hdeZkkZ.
class PapayaWhip: [K 1]
pass [K 2]
1. ↑ Определенный ur_deZkkbf__lbfy PapayaWhip и не наследует
никакой другой класс. Имена классо dZdijZило, пишутся с большой
буквы, НапримерВотТак , но это k_]hebrvkh]eZr_gb_Zg_lj_[hание.
2. ↑ Вы на_jgh_m`_^h]Z^ZebkvqlhdZ`^ZykljhdZ определении класса
имеет отступ, также как и kemqZ_knmgdpbyfbhi_jZlhjhfmkeh\gh]h
перехода if, циклом for или любым другим блоком кода. ПерZykljhdZ
без отступа находится вне блока class .
Класс PapayaWhip не содержит определений методов или атрибутоghk
точки зрения синтаксиса, тело класса не может остаZlvkyimkluf<lZdbo
случаях используется оператор pass . В языке Python pass —
зарезерbjhанное слово, которое гоhjblbgl_jij_lZlhjmb^_f^Zevr_
здесь ничего нет». Это оператор не делающий ровным счетом ничего, но тем
не менее яeyxsbckym^h[gufj_r_gb_fdh]^Zам нужно сделать заглушку
для функции или класса.

Выражение pass =в языке Python аналог пустого множестZbebnb]mjguo
скобок yaudZo Jav ~=или C+H K=
Многие классы наследуются от других классоghg_wlhlFgh]b_deZkku
определяют сhbf_lh^ughg_wlhlDeZkk Python не обязан иметь ничего,
кроме имени. В частности, людям знакомым с C++ может показаться
странным, что у класса в Python отсутстmxl\yном виде конструктор и
деструктор. Несмотря на то, что это не яey_lkyh[yaZl_evgufdeZkk\3\WKRQ
может иметь нечто, похожее на конструктор: метод __init__ ().
__init__() Метод
В следующем примере демонстрируется инициализация класса Fib , с
помощью метода __init__ ().

138

class Fib:
'''iterator that yields numbers in the Fibonacci sequence'''
[K 1]
def __init__ (self , max ): [K 2]
1. ↑ Классы, по аналогии с модулями и функциями могут (и должны) иметь
строки документации (docstrings).
2. ↑ Метод __init__ () uau\Z_lkykjZam`_ihke_kha^Zgbywda_fieyjZ
класса. Было бы заманчиhghnhjfZevghg_ерно, считать его
«конструктором» класса. ЗаманчиhihlhfmqlhhggZihfbgZ_l
конструктор класса yaud_ C++ : g_rg_ (общепринято, что метод
__init__ () должен быть пе рuff_lh^hfhij_^_e_gguf^eydeZkkZ b 
дейстbb (это перuc[ehddh^Zbkihegy_fuc контексте только что
созданного экземпляра класса). Неверно, потому что на момент uahа
__init__ () объект уже фактически является созданным, и ufh`_l_
оперироZlv корректной ссылкой на него ( self )
Первым аргументоex[h]hf_lh^ZdeZkkZключая метод __init__ (), всегда
яey_lkykkuedZgZl_dmsbcwda_fieyjdeZkkZIjbgylhgZauать этот
аргумент self . Этот аргумент uihegy_ljhevaZj_a_j\bjhанного слова this 
C++ или Java , но, тем не менее, в Python self не яey_lkyaZj_a_jироZgguf
Несмотря на то, что это всего лишь соглашение, пожалуйста не назыZcl_
этот аргумент как либо еще.
В случае метода __init__ (), self ссылается на только что созданный объект; 
осталь ных методах — на экземпляр, метод которого был uaан. И, хотя Zf
необходимо яghmdZauать self при определении метода, при uah\_wlh]hg_
требуется; Python добаbl_]h^ey\ZkZ\lhfZlbq_kdb.
Создание экземпляров
Для создания нового экземпляра класса в Python нужно uaать класс, как
если бы он был функцией, передаg_h[oh^bfu_Zj]mf_glu^eyf_lh^Z
__init__() . В качест_озjZsZ_fh]hagZq_gbyfuihemqbflhevdhqlh
созданный объект.
>>> import fibonacci2
>>> fib = fibonacci2. Fib (100 ) 1
>>> fib 2

>>> fib.__class__ 3

>>> fib.__doc__ 4

139

'iterator that yields numbers in the Fibonacci sequence'

1. Вы создаете ноucwda_fieyjdeZkkZ Fib (определенный fh^me_
fibonacci2 ) и присваиZ_l_lhevdhqlhkha^Zgguch[t_dli_j_f_gghc fib .
Единст_gguci_j_^ZggucZj]mf_glkhhlетстm_lbf_ghанному
аргументу max , f_lh^_ __init__() класса Fib .
2. fib теперь яey_lkywda_fieyjhfdeZkkZ Fib
3. Каждый экземпляр класса имеет kljh_ggu й атрибут __class__ , который
указыZ_lgZdeZkkh[t_dlZ-DYDijh]jZffbklufh]ml[ulvagZdhfuk
классом Class , который содержит методы getName() и getSuperclass() ,
используемые для получения информации об объекте. В Python,
метаданные такого рода доступны через соот_lklующие атрибуты, но
используемая идея та же самая.
4. Вы можете получить строку документации (docstring) класса, по аналогии
с функцией и модулем. Все экземпляры класса имеют одну и ту же строку
документации.

Для создания нового экземпляра кл асса в Python, просто uahите класс,
как если бы он был функцией, яgu_hi_jZlhjudZdgZijbf_j new =K
или Java, yaud_3\WKRQhlkmlklуют. =
Переменные экземпляра
Перейдем к следующей строчке:
class Fib:
def __init__ (self , max ):
self .max = max 1.
1. Что такое self .max ? Это переменная экземпляра. Она не имеет ничего
общего с переменной max , которую мы передали в метод __init__ () 
качест_Zj]mf_glZ self .max яey_lky]eh[Zevghc^eyсего
экземпляра. Это значит, что ufh`_l_h[jZlblvkydg_cba^jm]bo
методов.

class Fib:
def __init__ (self , max ):
self .max = max 1.
.
.
.
def __next__ (self ):
2 .

140

fib = self .a
if fib > self .max :
1. self .max определена в методе __init__ ()...
2. …и использоZgZ методе __next__ ().
Переменные экземпляра сyaZgulhevdhkh^gbfwda_fieyjhfdeZkkZ
Например, если вы создадите дZwda_fieyjZdeZkkZ Fib с разными
максимальными значениями, каждый из них будет помнить только сh_
собст_ggh_agZq_gb_.
>>> import fibonacci2
>>> fib1 = fibonacci2. Fib (100 )
>>> fib2 = fibonacci2. Fib (200 )
>>> fib1. max
100
>>> fib2. max
200
Итератор чисел Фибоначчи
Теперь u]hlhы узнать как создать итератор. Итератор это обычный класс,
который определяет метод __iter__ ().
class Fib: ①
def __init__ (self , max ): ②
self .max = max

def __iter__ (self ): ③
self .a = 0
self .b = 1
return self

def __next__ (self ): ④
fib = self .a
if fib > self .max :
raise StopIteration ⑤
self .a, self .b = self .b, self .a + self .b
return fib ⑥
① Чтобы построить итератор с нуля, Fib должна быть классом, а не функцией.

141

Подробнее об
итераторах

Погружение
HAWAII + IDAHO + IOWA + OHIO == STATES . Или, если записать это по -
другому, 510199 + 98153 + 9301 + 3593 == 621246. Думаете, я брежу? Нет, это
просто головоломка.
Позhevl_fg_ijbести разгадку.
HAWAII + IDAHO + IOWA + OHIO == STATES
510199 + 98153 + 9301 + 3593 == 621246

H = 5
A = 1
W = 0
I = 9
D = 8
O = 3
S = 6
T = 2
E = 4
Такие голоhehfdbgZauаются криптарифмами . Буквы состаeyxl
сущестmxsb_kehа, а если заменить каждую букву цифрой от 0 до 9,
получится еще и праbevgh_fZl_fZlbq_kdh_jZенство. Весь фокус в том,
чтобы uykgblvdZdZy[md\Zkhhl\_lklует каждой цифре. Все oh`^_gby
каждой буквы должны заменяться одной и той же цифрой, одной цифре не
могут соот_lklоZlvg_kdhevdh[md\bkeh\Zg_fh]mlgZqbgZlvkykpbnju
0.
В этой гла_fuihagZdhfbfkykihljykZxs_cijh]jZffhcgZyaud_3\WKRQ
написанной Рэймон дом Хейттингером. Эта программа решает
криптарифмические голоhehfdbbkhklhbl\k_]hbakljhddh^Z.

142

Наиболее известная криптарифмическая
головоломка SEND + MORE = MONEY.
import re
import itertools

def solve (puzzle ):
words = re .findall ('[A -Z]+' , puzzle. upper ())
unique_characters = set (''.join (words ))
assert len (unique_characters ) <= 10 , 'Too many letters'
first_letters = {word [0] for word in words }
n = len (first_letters )
sorted_characters = ''.join (first_letters ) + \
''.join (unique_characters - first_letters )
characters = tuple (ord (c) for c in sorted_characters )
digits = tuple (ord (c) for c in '0123456789' )
zero = digits [0]
for guess in itertools .permutations (digits , len (characters )):
if zero not in guess [:n ]:
equation = puzzle. translate (dict (zip (characters , guess )))
if eval (equation ):
return equation

if __name__ == '__main__' :
import sys
for puzzle in sys .argv [1:]:
print (puzzle )
solution = solve (puzzle )
if solution:
print (solution )
Вы можете запустить программу из командной строки. В Linux это будет
u]ey^_lvlZd <uiheg_gb_ijh]jZffufh`_lihlj_[hать некоторого
j_f_gbaZисящего от быстродейстbyy ашего компьютера, а индикатор
uiheg_gby\ijh]jZff_hlkmlklует. Поэтому просто наберитесь терпения .)
you@localhost:~/diveintopython3/examples$ python3 alphametics.py "HAWAII +
IDAHO + IOWA + OHIO == STATES"
HAWAII + IDAHO + IOWA + OHIO = STATES
510199 + 98153 + 9301 + 3593 == 621246
you@localhost:~/diveintopython3/examples$ python3 alphametics.py "I + LOVE +
YOU == DORA"

143

I + LOVE + YOU == DORA
1 + 2784 + 975 == 3760
you@localhost:~/diveintopython3/examples$ python3 alphametics.py "SEND +
MORE == MONEY"
SE ND + MORE == MONEY
9567 + 1085 == 10652
Нахождение всех вхождений шаблона
Первая _svdhlhjmx^_eZ_lwlZijh]jZffw — это находит k_keh\Z \
оригинале — буквы, так как ищет она именно буквы, прим. пере 
голоhehfd_.
>>> import re
>>> re .findall ('[0 -9]+' , '16 2 -by -4s in rows of 8' ) ①
['16' , '2' , '4' , '8' ]
>>> re .findall ('[A -Z]+' , 'SEND + MORE == MONEY' ) ②
['SEND' , 'MORE' , 'MONEY' ]
① Модуль re реализует регулярные ujZ`_gby\Iblhg_<wlhffh^me__klv
удобная функция findall(), которая принимает в качестве параметроrZ[ehg
регулярного ujZ`_gbybkljhdmbgZoh^blсе подстроки, соот_lklующие
шаблону. В этом случае шаблон соот_lk тm_lihke_^hательностям цифр.
Функция findall() возjZsZ_lkibkhdсех подстрок, предстаeyxsbo
последоZl_evghklvpbnj.
② Здесь регулярное ujZ`_gb_khhlетстm_lihke_^hательностям букв. И
сноZозjZsZ_fh_agZq_gb_ij_^klZляет собой список, каждый элемент
которого — это строка, соот_lklующая шаблону регулярного ujZ`_gby.
Это самая трудная скороговорка на
английском языке.
Вот другой пример, над которым Zfозможно, придется поломать голову.
>>> re .findall (' s.*? s' , "The sixth sick sheikh's sixth sheep's sick." )
[' sixth s' , " sheikh's s" , " sheep's s" ]
Удиe_gu"Ijb\_^_ggh_j_]meyjgh_ыражение ищет пробел, за которым
следуют буква s, кратчайшая hafh`gZyihke_^hательность любых симheh
(.*?), пробел и еще одна буква s.
1. The sixth s ick sheikh’s sixth sheep’s sick.
2. The sixth sick s heikh’s sixth sheep’s sick.
3. The sixth sick sheikh’s s ixth sheep’s sick.

144

4. The sixth sick sheikh’s sixth s heep’s sick.
5. The sixth sick sheikh’s sixth sheep’s s ick.
Но функция re.findall() находит только три oh`^_gbyi_jое, третье и пятое.
Почему так? Потому что она не ha\jZsZ_li_j_dju\Zxsb_kyih^kljhdb
Первая подстрока перекрыw ется со lhjhcihwlhfmbозjZsZ_lkylhevdh
перZykljhdZZ\lhjZyijhimkdZ_lkyAZl_flj_lvyih^kljhdZi_j_dju\Z_lky
с чет_jlhcihwlhfmозjZsZ_lkylhevdhlj_lvyih^kljhdZZq_lертая
пропускается. Наконец haращается пятая подстрока. Три соiZ^_g ия, а не
пять.
Это не имеет никакого отношения к решению криптарифмоyijhklh
подумал, что это интересно.
Нахождение уникальных элементов в последовательностях
МножестZ^_eZxlaZ^ZqmgZoh`^_gbymgbdZevguowe_f_glh\
последоZl_evghklbljbиальной.
>>> a_list = ['The' , 'sixth' , 'sick' , "sheik's" , 'sixth' , "sheep's" , 'sick' ]
>>> set (a_list ) ①
{'sixth' , 'The' , "sheep's" , 'sick' , "sheik's" }
>>> a_string = 'EAST IS EAST'
>>> set (a_string ) ②
{'A' , ' ', 'E' , 'I', 'S' , 'T' }
>>> words = ['SEND' , 'MORE' , 'MONEY' ]
>>> ''.join (words ) ③
'SENDMOREMONEY'
>>> set (''.join (words )) ④
{'E' , 'D' , 'M' , 'O' , 'N' , 'S' , 'R' , 'Y' }

① Получиkibkhdbag_kdhevdbokljhdnmgdpbyVHW ернет множестh
уникальных строк из этого списка. Работа этой функции будет более понятной,
если uij_^klZ\bl_pbdeIRU<havfbl_i_jый элемент из списка и добаvl_
его в множестh<lhjhcLj_lbcQ_ly ертый. Пятый — постойте -ка, он уже
есть в множестве, поэтому его не нужно добаeylvihlhfmqlhfgh`_klа 
Питоне не позheyxlbf_lvihторяющиеся элементы. Шестой. Седьмой —
сноZ^m[ebdZlijhimkdZ_f_]hDZdh\j_amevlZl"<k_mgbdZevgu_we_f_glu
исходно го списка без дубликатоBkoh^guckibkhd^Z`_khjlbjhать
предварительно не нужно.
② Тот же подход работает и если функции set() передать строку, а не список,
поскольку строка — это просто последовательность симheh.
③ Получиkibkhdkljhdnmgdpby .join (a_list) объединяет k_wlbkljhdb
одну.

145

④ Этот фрагмент кода, получиkibkhdkljhdозjZsZ_l\k_mgbdZevgu_
симheuстречающиеся h\k_okljhdZo.
Наша программа для решения криптарифмоbkihevam_lwlml_ogbdm^ey
получения множестZ\k_omgbdZevguo букв, используемых ]heh\hehfd_.
unique_characters = set (''.join (words ))

Проверка выполнения условий
Like many programming languages, Python has an assert statement. Here’s how it
works.
Подобно многим языкам программироZgby3\WKRQbf__lhi_jZlhj
подт_j`^_gbyhlkmlklия ошибок. Вот как он работает .
>>> assert 1 + 1 == 2 ①
>>> assert 1 + 1 == 3 ②
Traceback (most recent call last ):
File "" , line 1, in
AssertionError
>>> assert 2 + 2 == 5, "Only for very large values of 2" ③
Traceback (most recent call last ):
File "" , line 1, in
AssertionError : Only for very large values of 2

① За словом assert следует любое допустимое Iblhg_\ujZ`_gb_<
данном случае, выражение 1 + 1 == 2 haращает значение True, поэтому
assert ничего не делает.
② Однако если выражение haращает False, assert u[jZkuает
исключение.
③ Вы также можете добав ить информатиgh_khh[s_gb_dhlhjh_[m^_l
u\_^_ghijbозбуждении исключения AssertionError.
Поэтому , эта строка кода
assert len (unique_characters ) <= 10 , 'Too many letters'
экbалентна
if len (unique_characters ) > 10 :
raise AssertionError ('Too many letters' )

Программа для решения криптарифмоbkihevam_lbf_gghwlh\ujZ`_gb_^ey
того чтобы прекратить uiheg_gb__keb\]hehоломке содержится более

146

десяти уникальных букв. Поскольку каждая буква соот_lklует цифре, а цифр
k_]h^_kylv]hehоломка с более чем десятью уникальными буквами не
может иметь решения.
Выражения -генераторы
Выражения -генераторы, как генератор функций, но без функции.
>>> unique_characters = {'E' , 'D' , 'M' , 'O' , 'N' , 'S' , 'R' , 'Y' }
>>> gen = (ord (c) for c in unique_characters ) ①
>>> gen ②
at 0x00BADC10 >
>>> next (gen ) ③
69
>>> next (gen )
68
>>> tuple (ord (c) for c in unique_characters ) ④
(69 , 68 , 77 , 79 , 78 , 83 , 82 , 89 )
① Выражение -генератор, это как анонимная функция которая выдает
значения. Само выражение u]ey^bldZdkibkhdghhgh[_jgmlg_\
квадратные, а в фигурные скобки.
② Выражение -генератор haращает итератор.
③ Вызов next (gen ) haращает следующее значение итер атора.
④ Если ZfgjZ\blkyы можете поlhjblvq_j_aсе hafh`gu_agZq_gbyb
haратить кортеж, список или множестhgZijZив выражение -генератор 
tuple (), list (), или set (). В этих случаях Zfg_gm`gh^hihegbl_evguokdh[hd -
просто передайте "голые" выражение ord (c) for c in unique_characters  tuple ()
функцию, и Python понимает, что это ujZ`_gb| -генератор.

Использование ujZ`_gbc Jгенератороместо списка помогает
сохра нить cpu =и ram . Если ubkihevam_l_kibkhdqlh[uihlhfыбросить
его (например передать  tuple ()=или set ()), используйте генератор f_klh
него! =
Вот еще один способ сделать то же самое, используя генератор функции:
def ord_map (a_string ):
for c in a_string:
yield ord (c)

gen = ord_map (unique_characters )
Выражения -генераторы более компактны, но функционально равны.

147

Тестирование

(не) Погружение
(Данная страница находится на стадии переh^Z)
Соj_f_ggZyfheh^_`vBa[Zeh\Zgu[ukljufb компьютерами и модными
«динамическими» языками. Писать, потом предостаeylvkой код, потом
отлажиZlv \emqr_fkemqZ_ <gZrb^gb[ueZ^bkpbiebgZYkdZaZe
дисциплина! Нам приходилось писать программы jmqgmxgZ[mfZ]_b
одить их dhfivxl_jgZi_j фокартах. И нам это нраbehkv!
В этом разделе вы напишете и отладите набор kihfh]Zl_evguonmgdpbc^ey
кон_jlbjh\Zgby римскую систему и обратно. Вы b^_ebdZddhgkljmbjmxlky
и Zeb^bjmxlkyqbkeZ\jbfkdhckbkl_f_ разделе «Учебный пример:
Римские цифр ы». Вернемся немного назад и представим, как бы u]ey^beZ
реализация в b^_nmgdpbbijhba\h^ys_cij_h[jZahание в обоих
напраe_gbyo.
Правила формироZgbyjbfkdboqbk_eijbодят нас к нескольким интересным
наблюдениям:
1. Сущестm_llhevdhh^bgijZильный способ записать число римскими
цифрами.
2. Обратное также _jgh_kebkljhdZkbfолов яey_lky
последоZl_evghklvxjbfkdbokbf\heh\hgZij_^klZляет только одно
число, то есть может быть интерпретироZgZ_^bgklенным способом.
3. Диапазон чисел, которые могут быть записаны римскими цифрами, — от 1
до 3999. У римлян было несколько способов записыZlv[he__djmigu_qbkeZ
qZklghklbkihfhsvxq_jlugZ^qbkehfdhlhjZyhagZqZeZuqlhagZq_gb_
нужно умножить на 1000. Для целей этой глаugZf^hklZlhqghhz раничиться
диапазоном 1 — 3999.
4. Нет способа предстаblv\jbfkdhckbkl_f_.
5. Нет способа предстаblvhljbpZl_evgu_qbkeZ\jbfkdhckbkl_f_.
6. Нет способа предстаblv^jh[gu_bebg_p_eu_qbkeZ римской системе.
Попробуем отразить, что должен делать модуль roman.py. Он будет содержать
д_hkgh\gu_nmgdpbbWRBURPDQ bIURPBURPDQ NmgdpbyWRBURPDQ ^he`gZ

148

принимать целое число в диапазоне от 1 до 3999 и haращать строку,
содержащую римское предстаe_gb_wlh]h числа…
Останоbfkya^_kv>Z\Zcl_l_i_jvk^_eZ_fdh| -что неожиданное: опишем
небольшой тестовый случай, который про_jy_ljZ[hlZ_lebnmgdpby
to_roman() так, как мы этого ожидаем. Вы праbevghihgyebfukh[bjZ_fky
написать код, который тестирует код, кот орый еще не написан.
Это так назыZ_fZyjZajZ[hldZq_j_al_klbjhание (test -driven -development,
TDD). Набор из двух функций кон_jlZpbb — to_roman(), from_roman() —
может быть написан и протестироZgdZdxgbl\hl^_evghklbhlex[hc
крупной программы, кото рая импортирует их. В Python’е есть фреймhjd^ey
юнит -тестироZgbyfh^mevkkhhlетстmxsbfgZau\Zgb_fXQLWWHVW.
Юнит тестироZgb| — Z`gZyqZklvсей стратегии разработки, осноZgghcgZ
тестироZgbb?kebы пишете юнит -тесты, необходимо писать их на ра нних
этапах и обноeylvboihf_j_baf_g_gbcdh^Zblj_[hаний. Многие люди
пропагандируеют написание тестов до написания тестируемого кода, и именно
этот подход я собираюсь продемонстрировать wlhfjZa^_e_H^gZdhxgbl -
тесты u]h^guне заbkbfhklbhllh го, когда uboibr_l_.
 До написания кода написание юнит тестов застаey_l^_lZebabjhать
требоZgby\m^h[ghf^eyboj_ZebaZpbbиде
 Во j_fygZibkZgbydh^Zxgbll_kluij_^hojZgyxl\Zkhlebrg_]h
кодироZgbyDh]^Zсе тесты проходят, тестируемый юнит гот ов.
 Во j_fyj_nZdlhjbg]Zhgbihfh]Zxl^hdZaZlvqlhghая _jkbyедет
себя так же, как и старая.
 Во j_fyih^^_j`dbdh^Zkms_klование юнит тестов прикроет Zrm
задницу, когда кто -то начнет кричать, что Zr_ihke_^g__baf_g_gb_
сломало их код. («Но сэр, все тесты проходили успешно, когда я делал
commit.»)
 Когда код пишется dhfZg^_gZebqb_сестороннего набора тесто
значительно снижает риск того что ваш код сломает код другого
разработчика, поскольку ufh`_l_bkihegblv_]hxgbll_klu Yидел
как это работает на практике FRGHVSULQWV dh^bg]gZkdhjhklv? :) ???).
Команда разбиZ_laZ^Zgb_mqZklgbdbjZa[bjZxlki_pbnbdZpbbkоих
задач, пишут для них юнит тесты, затем обмениZxlkyxgbll_klZfbkh
k_cdhfZg^hcLZdgbdlhg_aZc^_lkebrdhf^Ze_dh\jZ~ работке кода
который плохо пригоден для команды.)
Единственный вопрос.
Один тестоuckemqZc WHVWFDVH hlечает на один hijhkhl_klbjm_fhfdh^_
ТестоuckemqZc^he`_g[ulvkihkh[_g…
 … запускаться самостоятельно, без ода данных от человека. Юнит
тести роZgb_^he`gh[ulvZ\lhfZlbabjhано

149

 … определять самостоятельно, прошла ли тестируемая функция тест
или нет, без f_rZl_evklа человека с целью интерпретироZlv
результаты
 … запускаться baheypbbhl^_evghhlhklZevguol_klhых случае
(даже если они тес тируют те же функции)
Каждый тестоuckemqZc — это остро.
УчитыZywlh^Z\Zcl_khklZим тестоuckemqZc l_kl ^eyi_jого
требоZgbyNmgdpbyWRBURPDQ ^he`gZозjZsZlvij_^klklZление числа
jbfkdhckbkl_f_kqbke_gby^ey\k_oqbk_ehl^h9
Н е сразу ясно, как этот скрипт делает... ну, хоть что -то. Он определяет класс,
не содержащий метод __init__(). Класс содержит другой метод, который
никогда не uau\Z_lkyKdjbilkh^_j`bl[ehdBBPDLQBBghlhlg_kkueZ_lky
на класс или его методы. Но кое -что он делает, по_jvl_fg_.
import roman1
import unittest
class KnownValues (unittest .TestCase ): ①
known_values = ( (1, 'I'),
(2, 'II' ),
(3, 'III' ),
(4, 'IV' ),
(5, 'V' ),
(6, 'VI' ),
(7, 'VII' ),
(8, 'VIII' ),
(9, 'IX' ),
(10 , 'X' ),
(50 , 'L' ),
(100 , 'C' ),
(500 , 'D' ),
(1000 , 'M' ),
(31 , 'XXXI' ),
(148 , 'CXLVIII' ),
(294 , 'CCXCIV' ),
(312 , 'CCCXII' ),
(421 , 'CDXXI' ),
(528 , 'DXXVIII' ),
(621 , 'DCXXI' ),
(782 , 'DCCLXXXII' ),
(870 , 'DCCCLXX' ),
(941 , 'CMXLI' ),
(1043 , 'MXLIII' ),

150

(1110 , 'MCX' ),
(1226 , 'MCCXXVI' ),
(1301 , 'MCCCI' ),
(1485 , 'MCDLXXXV' ),
(1509 , 'MDIX' ),
(1607 , 'MDCVII' ),
(1754 , 'MDCCLIV' ),
(1832 , 'MDCCCXXXII' ),
(1993 , 'MCMXCIII' ),
(2074 , 'MMLXXIV' ),
(2152 , 'MMCLII' ),
(2212 , 'MMCCXII' ),
(2343 , 'MMCCCXLIII' ),
(2499 , 'MMCDXCIX' ),
(2574 , 'MMDLXXIV' ),
(2646 , 'MMDCXLVI' ),
(2723 , 'MMDCCXXIII' ),
(2892 , 'MMDCCCXCII' ),
(2975 , 'MMCMLXXV' ),
(3051 , 'MMMLI' ),
(3185 , 'MMMCLXXXV' ),
(3250 , 'MMMCCL' ),
(3313 , 'MMMCCCXIII' ),
(3408 , 'MMMCDVIII' ),
(3501 , 'MMMDI' ),
(3610 , 'MMMDCX' ),
(3743 , 'MMMDCCXLIII' ),
(3844 , 'MMMDCCCXLIV' ),
(3888 , 'MMMDCCCLXXXVIII' ),
(3940 , 'MMMCMXL' ),
(3999 , 'MMMCMXCIX' )) ②
def test_to_roman_known_values (self ): ③
'''to_roman should give known result with known input'''
for integer , numeral in self .known_values :
result = roman1. to_roman (integer ) ④
self .assertEqual (numeral , result ) ⑤
if __name__ == '__main__' :
unittest .main ()
① Для описания тестового случая перuf^_ehfhij_^_ebfdeZkk7HVW&DVH
подкласс модуля unittest. Этот класс содержит много полезных методов,
которые ufh`_l_bkihevahать в Zrbol_klZo^eyhij_^_e_gguomkeh\bc.

151

② Это множестhiZjqbkehagZq_gb_hij_^_e енных мной jmqgmxHgh
dexqZ_lfbgbfZevgu_qbk_egZb[hevr__  се числа, которые 
преобразоZgghfиде состоят из одного симheZZlZd`_gZ[hjkemqZcguo
чисел. Не нужно тестировать все hafh`gu_арианты, но k_mgbdZevgu_
ZjbZgluijhl_klbjh Zlvgm`gh.
③ Каждый тест определен отдельным методом, который uau\Z_lky[_a
параметроbg_\haращает значения. Если метод за_jrZ_lkyghjfZevgh
без выброса исключения - тест считается пройденным, если u[jhr_gh
исключение - тест заZe_g.
④ Здесь и происходит uah тестируемой функции to_roman(). (Ну, функция
еще не написана, но когда будет, это будет строка, которая ее uahет.)
Заметьте, что Вы только что определили интерфейс (API) функции to_roman():
она должна принимать число для кон_jl ирования и возjZsZlvkljhdm
(престаe_gb_ виде Римского числа). Если API отличается от
ur_mdZaZggh]hl_klернет ошибку. Также отметьте, что Вы не отлавлиZ_l_
какие -либо исключения, когда uauаете to_roman(). Это сделано специально.
to_roman() не до лжна haращать исключение при uahе с праbevgufb
oh^gufbiZjZf_ljZfbbijZильными значениями этих параметро?keb
to_roman() u[jZkuает исключение, Тест считается проZe_gguf.
⑤ Предполагая, что функция to_roman() определена корректно, ua\ZgZ
корр ектно, uihegbeZkvmki_rghbернула значение, последним шагом
будет про_jdZijZильности возjZs_ggh]hagZq_gbyWlhh[sbcопрос,
поэтому используем метод AssertEqual класса TestCase для про_jdb
ра_gklа (эквиZe_glghklb ^ух значений. Если haраще нный функцией
to_roman() результат (result)не ра_gba\_klghfmagZq_gbxdhlhjh_<u
ожидаете (numeral), assertEqual u[jhkblbkdexq_gb_bl_klaZершится с
ошибкой. Если значения экbаленты, assertEqual ничего не сделает. Если
каждое значение, haращенно е to_roman() соiZ^_lkh`b^Z_fufbaестным,
assertEqual никогда не u[jhkblbkdexq_gb_ZagZqbl
test_to_roman_known_values blh]_ыполнится нормально, что будет
означать, что функция to_roman() успешно прошла тест.
Раз у Вас есть тест, Вы можете написа ть саму функцию to_roman(). Во -перuo
Вам необходимо написать заглушку, пустую функцию и убедиться, что тест
проZeblky?kebl_klm^Zq_gdh]^Znmgdpby_s_gbq_]hg_^_eZ_lagZqbl
тест не работает вообще! Unit testing это как танец: тест _^_ldh^ke_^m ет.
Пишете тест, который проZebается, потом - код, пока тест не пройдет.
# roman1.py
def to_roman (n):
'''convert integer to Roman numeral'''
pass ①
① На этом этапе Вы определяете API для функции to_roman(), но пока не
хотите писать ее код. (Для перhcijhерки теста.) Чтобы заглушить

152

функцию,используется зарезерbjh\Zggh_keh\h3\WKRQ - pass, которое...
ничего не делает. Выполняем romantest1.py on bg терпретаторе для про_jdb
теста. Если Вы uaали скрипт с параметром -v, будет u\_^_gih^jh[ghklbh
работе скрипта (verbose), и Вы сможете подробно увидеть, что происходит в
каждом тесте. Если по_aeh , уb^bl| нечто подобное :
you @ localhost: ~/diveintopython3/examples$ python3 romantest1. py -v
test_to_roman_known_values (__main__ .KnownValues ) ①
to_roman should give known result with known input ... FAIL ②
================================================================
FAIL: to_roman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest1.py" , line 73 , in test_to_roman_known_values
self .assertEqual (numeral , result )
AssertionError : 'I' != None ③
----------------------------------------------------------------------
Ran 1 test in 0.016s ④
FAILED (failures =1) ⑤
① Запущенный скрипт uihegy_l метод unittest .main (), который запускает
каждый тестоuc случай . Каждый тестоuckemqZc - метод класса в
romantest.py. Нет особых требоZgbcdhj]ZgbaZpbbwlbodeZkkh; они могут
быть как класс с методом для отдельного тестоh]hkemqZyPIUbh^bgdeZkk
несколько методо^ey\k_ol_klh\uokemqZ_. Необходимо лишь, чтобы
кажд ый класс был наследником unittest.TestCase.
② Для каждого тестоh]hkemqZyfh^mevXQLWWHVWыведет строку документации
метода и результат - успех или проZeDZdидно, тест проZe_g.
③ Для каждого проZe_ggh]hl_klZkbkl_fZыводит детальную информацию
о том, что конкретно произошло. В данном случае uah assertEqual() вызZe
ошибку объяe_gby $VVHUWLRQ(UURU ihkdhevdmh`b^ZehkvозjZs_gby
,
hl
to_roman(1), но этого не произошло. (Если у функции нет нет яgh]hозjZlZ
то она _jg_l1RQHagZq_gb_Qu ll 3\WKRQ)
④ После детализации каждого тестоh]hkemqZyXQLWWHVWhlh[jZ`Z_l
суммарно, сколько тесто[uehыполнено и сколько это заняло j_f_gb.
⑤ В целом тест считается проZe_gguf_kebohlvh^bgkemqZcg_ijhc^_g
Unittest различает ошибки и проZe ы. ПроZeызыZ_lf_lh^DVVHUW;<=
например assertEqual или assertRaises, который провалится, если
объяe_ggh_mkehие не_jghbebh`b^Z_fh_bkdexq_gb_g_\u[jhr_gh
Ошибка - это другой тип исключения, который выбрасыZ_lkyl_klbjm_fuf
кодом или тестовым юнитом и не яey_lkyh`b^Z_fuf.
Наконец, мы можем написать функцию to_roman().

15 3

roman_numeral_map = (('M' , 1000 ),
('CM' , 900 ),
('D' , 500 ),
('CD' , 400 ),
('C' , 100 ),
('XC' , 90 ),
('L' , 50 ),
('XL' , 40 ),
('X' , 10 ),
('IX' , 9),
('V' , 5),
('IV' , 4),
('I', 1)) ①
def to_roman (n):
'''convert integer to Roman numeral'''
result = ''
for numeral , integer in roman_numeral_map:
while n >= integer: ②
result + = numeral
n -= integer
return result
① roman_numeral_map - это кортеж кортежей, определяющий три _sb
предстаe_gb_[Zahых симheh римских цифр и популярных их сочетаний;
порядок римских симheh (h[jZlghfgZijZлении, от M и до I); значения
римских цифр. Каждый gmlj_ggbcdhjl_` - это па ра значений
(предстаe_gb_qbkeh Bwlhg_lhevdhh^ghkbfольные римские цифры; это
также пары симheh типа CM (“тысяча без сотни”). Это сильно упрощает код
функции to_roman().
② Вот здесь b^gh\q_fыигрыш такой структуры roman_numeral_map,
поскольк у не требуется какой -то хитрой логики при обработке uqblZgb_f>ey
кон_jlbjh\Zgby римское число необходимо просто пройти в цикле
roman_numeral_map, находя наименьшее число, в которое e_aZ_lhklZlhd
ода. При нахожднии такового, к возjZsZ_fhfmagZq_g ию функции
добаey_lkykhhlетстmxs__jbfkdh_ij_^klZление, од уменьшается на
это число и далее операция поlhjy_lky^eyke_^mxs_]hdhjl_`Z.
Если все же не понятно, как работает функция to_roman(), добаbfSULQW 
конец цикла:
while n >= integer:
result + = numeral
n -= integer
print ('subtracting {0} from input, adding {1} to output' .format (integer , numeral ))

154

Этот отладочный u\h^ihdZau\Z_lke_^mxs__:
>>> import roman1
>>> roman1. to_roman (1424 )
subtracting 1000 from input , adding M to output
subtracting 400 from input , adding CD to output
subtracting 10 from input , adding X to output
subtracting 10 from input , adding X to output
subtracting 4 from input , adding IV to output
'MCDXXIV'
Ну,функция to_roman() jh^_[ujZ[hlZ_ldZdb предполагалось в начале
глаuGh пройдет ли она написанный ранее тест ?
you @ localhost: ~/diveintopython3/examples$ python3 romantest1. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
----------------------------------------------------------------------
Ran 1 test in 0.016s
OK
1. Ура ! Функция to_roman() прошла тест “known values”. Возможно не
k_klhjhggyyijhерка, но __oh^_ijhерены различные oh^gu_^Zggu_
dexqZyqbkeZ записыZ_fu_h^gbfjbfkdbfkbfолом, наибольшее
исходное значение (3999), и значение, дающее наибольшее римское число
(3888). На этом этапе можно сказать, что функция корректно обрабатывает
любые праbevgu_bkoh^gu_agZq_gby.
“Праbevgu_bkoh^gu_agZq_gby ? Хм. А как насчет непраbevguo?
«Остановись и загорись»
Недостаточно про_jblvjZ[hlmnmgdpbblhevdhkijZильными oh^gufb
данными; также необходимо убедиться, что функция выдаст ошибку при
непраbevghfh^__ не просто ошибку - а такую как ожидается .
>>> import roman1
>>> roman1. to_roman (4000 )
'MMMM'
>>> roman1. to_roman (5000 )
'MMMMM'
>>> roman1. to_roman (9000 ) ①
'MMMMMMMMM'
1. Это определенно не то, что ожидалось — это не праbevgu_jbfkdb_qbkeZ
По сути, k_wlbqbkeZыходят за возможные пределы, но функция k_jZно
haращает результат, только фиктиgucLboh_озjZs_gb_g_ерного

155

значения - ооооооооооочень не_jgh_ke и возникает ошибка, лучше чтобы
программа за_jrZeZkv[ukljhbrmfghHklZghись и загорись", как
гоhjblkyIblhghский" способ останоblvkybaZ]hj_lvky - это u[jhkblv
исключение.
Спрашивается, как же учесть это в требоZgbyodl_klbjhанию? Для
начин ающих - hllZdnmgdpbyWRBURPDQ ^he`gZыбрасыZlvbkdexq_gb_
типа OutOfRangeError, если ей передать число более 3999. Как будет
u]ey^_lv тест ?
class ToRomanBadInput (unittest .TestCase ): ①
def test_too_large (self ): ②
'''to_roman should fail with large input'''
self .assertRaises (roman2. OutOfRangeError , roman2. to_roman , 4000 ) ③
1. Как и в предыдущем случае, создаем класс -наследник от unittest.TestCase. У
Вас может быть более одного теста на класс (как Вы уb^bl_^Zevr_\wlhc
гла_ ghyj_rbekha^Zlvhl^_evgucdeZkk^eywlh]hihlhfmqlhwlhlkemqZc
отличается от предыдущих. Мы поместили k_l_klugZiheh`bl_evguc
uoh^ одном классе, а на ошибки - ^jm]hf.
2. Как и в предыдущем случае, тест - это метод, имя которого - назZgb_l_klZ.
3. Класс unittest.TestCase предостаey_lf_lh^DVVHUW5DLVHVdhlhjuc
принимает следующие ар гументы: тип ожидаемого исключения, имя
тестируемой функции и аргументы этой функции. (Если тестируемая функция
принимает более одного аргумента, k_hgbi_j_^Zxlkyf_lh^mDVVHUW5DLVHV
по порядку, как будто передаете их тестируемой функции.)
Обратите особо е gbfZgb_gZihke_^gxxkljhdmdh^Z<f_klhызоZ
функции to_roman() и про_jdbручную того, что она u[jZkuает
исключение (путем обертыZgby__ блок try -catch), метод assertRaises
делает все это за нас. Все что Вы делаете - гоhjbl_dZdhclbibkdexq ения
ожидаете (roman2.OutOfRangeError), имя функции (to_roman()), и ее аргументы
(4000). Метод assertRaises позаботится о uahе функции to_roman() и
про_jblqlhhgZозjZsZ_lbkdexq_gb_URPDQ2XW2I5DQJH(UURU.
Также заметьте, что Вы передаете функцию t o_roman() как аргумент; Вы не
uauаете ее и не передаете ее имя как строку. Кажись, я уже упоминал, что
k_\3\WKRQyляется объектом?
Что же происходит, когда Вы запускаете скрипт с новым тестом?
you @ localhost: ~/diveintopython3/examples$ python3 romante st2. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... ERROR ①
================================================================

156

ERROR: to_roman should fail with large input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest2.py" , line 78 , in test_too_large
self .assertRaises (roman2. OutOfRangeError , roman2. to_roman , 4000 )
AttributeError : 'module' object has no attribute 'OutOfRangeError' ②
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors =1)
1. СледоZehh`b^Zlvwlhlijhал (если конечно Вы не написали
дополнительного кода), но… это не соk_fijhал", скорее это ошибка. Это
тонкое но очень важное различие. Тест может _jgmlvljbkhklhygbymki_o
проZebhrb[dmMki_o_kl_klенно, означает, что тест пройден — код
делает что положено. «Провал» - то что _jgmel_kl\ur| — код uihegy_lky
но haращает не ожидаемое значение. «Ошибка» означает, что Ваш код
работает непраbevgh.
2. Почему код не выполняется праbevgh"JZkdjmldZkl_dZy се объясняет.
Тестируемый модуль не u[jZkuает исключение типа OutOfRangeError. То
самое, которое мы скормили методу assertRaises(), потому что ожидаем его
при оде большого числа. Но исключение не u[jZkuается, потому uah
метода assertRaises() проZe ен. Без шансов - функция to_roman() никогда не
u[jhkbl2XW2I5DQJH(UURU.
Решим эту проблему - определим класс исключения OutOfRangeError в
roman2.py.
class OutOfRangeError (ValueError ): ①
pass ②
1. Исключения - это классы. Ошибка «out of range» это разноb^ghklv
ошибки — аргумент uoh^blaZ^himklbfu_ij_^_euIhwlhfmwlhbkdexq_gb_
наследуется от исключения ValueError. Это не строго необходимо (по идее
достаточно наследования от класса Exception), однако так праbevgh.
2. Исключение h[s_f -то ничего и не делает, но Вам нужна хотя бы одна
строка в классе. Встроенная функция pass ничего не делает, однако
необходима для минимального определения кода 3\WKRQ.
Теперь запустим тест еще раз.
you @ localhost: ~/diveintopython3/examples$ python3 romantest2. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... FAIL ①
===================================================== ===========

157

FAIL: to_roman should fail with large input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest2.py" , line 78 , in test_too_large
self .assertRaises (roman2. OutOfRangeError , roman2. to_roman , 4000 )
AssertionError : OutOfRangeError not raised by to_roman ②
----------------------------------------------------------------------
Ran 2 tests in 0.016s
FAILED (failures =1)
1. Тест по -прежнему не проходит, хотя уже и не u^Z_lhrb[dmWlhijh]j_kk
Значит, метод assertRaises() был uiheg_gbl_klnmgdpbbWRBURPDQ [ue
произ_^_g.
2. Конечно, функция to_roman() не выбрасыZ_llhevdhqlhhij_^_e_ggh_
исключение OutOfRangeError, так как Вы ее еще "не застаbebBwlhohjh шие
ноhklbAgZqbll_kljZ[hlZ_lZijhалиZlvkyhg[m^_lihdZ<ug_
напишете условие его успешного прохождения.
Этим и займемся .
def to_roman (n):
'''convert integer to Roman numeral'''
if n > 3999 :
raise OutOfRangeError ('number out of range (must be less than 4000)' ) ①
result = ''
for numeral , integer in roman_numeral_map:
while n >= integer:
result + = numeral
n -= integer
return result
1. Все просто: если переданный параметр больше 3999, u[jZkuаем
исключение OutOfRangeError. Тест не ищет текстоmxkljhdmh[tykgyxsmx
причину исключения, хотя Вы можете написать тест для про_jdbwlh]h gh
учтите трудности, сyaZggu_kjZaebqgufbyaudZfb - длина строк или
окружение могут отличаться).
Позh лит ли это пройти тест? Узнаем :
you @ localhost: ~/diveintopython3/examples$ python3 romantest2. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... ok ①
----------------------------------------------------------------------

158

Ran 2 tests in 0.000s
OK
1. Ура! Оба теста пройдены. Так как Вы работали, переключаясь между
кодироZgb_fbl_klbjh\Zgb_flh<uk у_j_gghklvxfh`_l_kdZaZlvqlh
именно последние 2 строки кода позhebebl_klmернуть "успех", а не
"проZeLZdZym\_j_gghklv^ZeZkvg_^_r_о, но окупит себя с лихhc
дальнейшем.
Больше СТОПов, больше "Огня"
Наряду с тестированием на слишком большой "од" необходимо
протестировать и слишком маленький. Как было отмечено lj_[hаниях к
функциональности,римские цифры не могут быть меньше или раgu.
>>> import roman2
>>> roman2. to_roman (0)
''
>>> roman2. to_roman (-1)
''
Не хорошо. Добавим тесты для каждого случая.
class ToRomanBadInput (unittest .TestCase ):
def test_too_large (self ):
'''to_roman should fail with large input'''
self .assertRaises (roman3. OutOfRangeError , roman3. to_roman , 4000 ) ①

def test_zero (self ):
'''to_roman should fail with 0 input'''
self .assertRaises (roman3. OutOfRangeError , roman3. to_roman , 0) ②

def test_negative (self ):
'''to_roman should fail with negative input'''
self .assertRaises (roman3. OutOfRangeError , roman3. to_roman , -1) ③
1. Метод test_too_large() не изменился. Я dexqbe_]hkx^Zqlh[uihdZaZlv
схожесть кода.
2. Это новый тест: test_zero(). Как и test_too_large(), мы застаey_ff_lh^
assertRaises(), определенный XQLWWHVW7HVW&DVHызZlvgZrmnmgdpbx
to_roman() с параметро м "0", и проверить, что она u[jZkuает
соответстmxs__bkdexq_gb_2XW2I5DQJH(UURU.
3. Метод test_negative() почти аналогичный, только передает -1 в функцию
to_roman(). И ни один из этих методов не _jg_lhrb[dm2XW2I5DQJH(UURU

159

(потому что наша функция h зjZsZ_lagZq_gb_ bl_klkqblZ_lky
проZe_gguf.
Теперь про_jbfqlhl_klijhалится:
you @ localhost: ~/diveintopython3/examples$ python3 romantest3. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_negative (__main__ .ToRomanBadInput )
to_roman should fail with negative input ... FAIL
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... ok
test_zero (__main__ .ToRomanBadInput )
to_roman should fail with 0 input ... FAIL
================================================================
FAIL: to_roman should fail with negative input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest3.py" , line 86 , in test_negative
self .assertRaises (roman3. OutOfRangeError , roman3. to_roman , -1)
AssertionError : OutOfRangeError not raised by to_roman
================================================================
FAIL: to_roman should fail with 0 input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest3.py" , line 82 , in test_zero
self .assertRaises (roman3. OutOfRangeError , roman3. to_roman , 0)
AssertionError : OutOfRangeError not raised by to_roman
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures =2)
Великолепно. Оба теста проZe_gudZdbh`b^Zehkv:l_i_jvh[jZlbfkyd
коду и посмотрим, что можно сделать для успешного п рохождения теста.
def to_roman (n):
'''convert integer to Roman numeral'''
if not (0 < n < 4000 ): ①
raise OutOfRangeError ('number out of range (must be 1..3999)' ) ②

result = ''
for numeral , integer in roman_numeral_map:
while n >= integer:
result + = numeral

160

n -= integer
return result
1. Отличный пример сокращения Python: множест_ggh_kjZнение в одну
строку. Это экbалентно выражению "Если не ((0 < n) и (n < 4000))", но
читается проще. Этот однострочный код охZluает "плохой" диапазон
oh^guo^Zgguo.
2. Изменение условия требует изменения сообщения исключения. Тестовому
фрэймhjdmсе раghZот при отладке jmqgmxfh]mlозникнуть трудности,
если сообщение будет непраbevghhibkuать ситуацию.
Я мог бы пр и_klbp_eucjy^ijbf_jh, чтобы показать, что однострочный
код работает, но вместо этого я просто запущу тест.
you @ localhost: ~/diveintopython3/examples$ python3 romantest3. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_negative (__main__ .ToRomanBadInput )
to_roman should fail with negative input ... ok
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... ok
test_zero (__main__ .ToRomanBadInput )
to_roman should fail with 0 input ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.016s
OK
И еще одна штука...
Еще одно требоZgb_dnmgdpbhgZevghklb - обработка нецелых чисел.
>>> import roman3
>>> roman3. to_roman (0.5 ) ①
''
>>> roman3. to_roman (1.0 ) ②
'I'
1. О, это плохо.
2. О, а это еще хуже.
Оба случая должны u[jhkblvbkdexq_gb_<f_klhwlh]hnmgdpbyозвращает
ложное значение.
ТестироZgb_g| -чисел _kvfZkeh`gh<h -перuohij_^_ebf исключение
NotIntegerError.

161

# roman4.py
class OutOfRangeError (ValueError ): pass
class NotIntegerError (ValueError ): pass
Далее напишем тестоuckemqZc^eyijhерки u[jhkZbkdexq_gby
NotIntegerError.
class ToRomanBadInput (unittest .TestCase ):
.
.
.
def test_non_integer (self ):
'''to_roman should fail with non -integer input'''
self .assertRaises (roman4. NotIntegerError , roman4. to_roman , 0.5 )
Убеждаемся , что тест проZe_g .
you @ localhost: ~/diveintopython3/examples$ python3 romantest4. py -v
test_to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_negative (__main__ .ToRomanBadInput )
to_roman should fail with negative input ... ok
test_non_integer (__main__ .ToRomanBadInput )
to_roman should fail with non -integer input ... FAIL
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... ok
test_zero (__main__ .ToRomanBadInput )
to_roman should fail with 0 input ... ok
================================================================
FAIL: to_roman should fail with non -integer input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest4.py" , line 90 , in test_non_integer
self .assertRaises (roman4. NotIntegerError , roman4. to_roman , 0.5 )
AssertionError : NotIntegerError not raised by to_roman
----------------------------------------------------------------------
Ran 5 tests in 0.000s
FAILED (failures =1)
Пишем код для прохождения теста.
def to_roman (n):
'''convert integer to Roman numeral'''
if not (0 < n < 4000 ):
raise OutOfRangeError ('number out of range (must be 1..3999)' )
if not isinstance (n, int ): ①

162

raise NotIntegerError ('non -integers can not be converted' ) ②
result = ''
for numeral , integer in roman_numeral_map:
while n >= integer:
result + = numeral
n -= integer
return result
1. Встроенная функция isinstance() про_jy_lijbgZ^e_`bleb переменная
определенному типу (точнее, технически - к наследнику типа).
2. Если аргумент n не число, u[jZkuаем наше ноh_bkdexq_gb_
NotIntegerError.
Наконец, про_jbfdh^gZl_kl_.
you @ localhost: ~/diveintopython3/examples$ python3 romantest4. py -v
test_ to_roman_known_values (__main__ .KnownValues )
to_roman should give known result with known input ... ok
test_negative (__main__ .ToRomanBadInput )
to_roman should fail with negative input ... ok
test_non_integer (__main__ .ToRomanBadInput )
to_roman should fail with non -integer input ... ok
test_too_large (__main__ .ToRomanBadInput )
to_roman should fail with large input ... ok
test_zero (__main__ .ToRomanBadInput )
to_roman should fail with 0 input ... ok
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
Функция to_roman() упешно прошла k_l_klub[hevr_l_klh\fg_ голову
не приходит, так что пора переходить к функции from_roman().
Приятная симметрия
Преобразование римского представления числа в десятичное u]ey^bl[he__
сложным, чем преобразоZgb_^_kylbqghcnhjfu\jbfkdmxHkgh\gZy
сложность заключается \Zeb^Zpbb>hklZlhqghijhklhijhерить, яey_lky
ли целое число положительным; однако немного с ложнее проверить, является
ли строка корректным римским числом. К счастью, мы уже написали
регулярное ujZ`_gb_ijhеряющее римские числа.
Осталась задача преобразоZgbykZfhckljhdbDZdfumидим через минуту,
благодаря определенной нами структуре данны х, ставящей khhl\_lkl\b_
целым числам римские, реализация функции from_roman() яey_lky
триbZevghcaZ^Zq_c.

163

Но сначала тесты. Нам понадобятся из_klgu_agZq_gby^eyыборочной
про_jdbijZильности кон_jlbjh\Zgby<dZq_klе этих значений мы будем
испо льзоZlvhibkZggucjZg__gZ[hjNQRZQBYDOXHV:
def test_from_roman_known_values (self ):
'''from_roman should give known result with known input'''
for integer , numeral in self .known_values :
result = roman5. from_roman (numeral )
self .assertEqual (integer , result )
Здесь мы наблюдаем интересную симметрию. Функции to_roman() и
from_roman() яeyxlky\aZbfhh[jZlgufbI_j\Zyij_h[jZam_l^_kylbqgh_
предстаe_gb_qbkeZ\jbfkdh_торая же делает обратное преобразование.
В теории мы должны имет ь hafh`ghklvaZfdgmlvdjm]i_j_^Z\nmgdpbb
to_roman() число, затем передать результат uiheg_gbynmgdpbb
from_roman(), ha\jZs_ggh_agZq_gb_dhlhjhc^he`ghkhпасть bkoh^guf
числом:
n = from_roman (to_roman (n)) for all values of n
В этом случае “all values” означает любое число bgl_jале [1, 3999].
Напишем тест, который передает все числа из этого интерZeZnmgdpbb
to_roman(), затем uauает from_roman() и про_jy_lkhhlетстb_j_amevlZlZ
исходному числу:
class RoundtripCheck (unittest .TestCase ):
def test_roundtrip (self ):
'''from_roman(to_roman(n))==n for all n'''
for integer in range (1, 4000 ):
numeral = roman5. to_roman (integer )
result = roman5. from_roman (numeral )
self .assertEqual (integer , result )
Наши ноu_ тесты пока не яeyxlky^Z`_ijhальными - они за_jrbebkvk
ошибкой, так как мы еще не реализоZebnmgdpbxIURPBURPDQ :
you @ localhost: ~/diveintopython3/examples$ python3 romantest5. py
E. E....
================================================================
ERROR: test_from_roman_known_values (__main__ .KnownValues )
from_roman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest5.py" , line 78 , in test_from_roman_known_values
result = roman5. from_roman (numeral )
AttributeError : 'module' object has no attribute 'from_roman'
================================================================

164

ERROR: test_roundtrip (__main__ .RoundtripCheck )
from_roman (to_roman (n))== n for all n
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest5.py" , line 103 , in test_roundtrip
result = roman5. from_roman (numeral )
AttributeError : 'module' object has no attribute 'from_roman'
----------------------------------------------------------------------
Ran 7 tests in 0.019s
FAILED (errors =2)
Создание заглушки функции решит эту проблему:
# roman5.py
def from_roman (s):
'''convert Roman numeral to integer'''
(Вы заметели? Я написал функцию, dhlhjhcg_lgbq_]hdjhf_kljhdb
документации. Это нормально. Это Python. На самом деле, многие
разработчики придержиZxlkybf_gghlZdh]hklbeyG_^_eZcaZ]emr_d
документируй!”)
Теперь тесты дейстbl_evghyляются проZevgufb:
you @ localhost: ~/diveintopython3/examples$ python3 romantest5. py
F. F....
================================================================
FAIL: test_from_roman_known_values (__main__ .KnownValues )
from_roman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest5.py" , line 79 , in test_from_roman_known_values
self .assertEqual (integer , result )
AssertionError : 1 != None
================================================================
FAIL: test_roundtrip (__main__ .RoundtripCheck )
from_roman (to_roman (n))== n for all n
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest5.py" , line 104 , in test_roundtrip
self .assertEqual (integer , result )
AssertionError : 1 != None
----------------------------------------------------------------------
Ran 7 tests in 0.002s
FAILED (failures =2)

165

Теперь напишем функцию from_roman():
def from_roman (s):
"""convert Roman numeral to integer"""
result = 0
index = 0
for numeral , integer in roman_numeral_map:
while s[index:index+ len (numeral )] == numeral: ①
result + = integer
index + = len (numeral )
return result
Стиль написания здесь точно такой же, как и в функции to_roman(). Мы
пробегаем все значения roman_numeral_map, но f_klhlh]hqlh[u[jZlv
максимальное целое число, пока это hafh`ghfu[_j_ffZdkbfZevgh_
римское представление чи сла и ищем его kljhd_ihdZwlh\hafh`gh.
Если Zf_s_g_khсем понятно, как работает функция from_roman(),
добаvl_ывод dhgp_pbdeZ:
def from_roman (s):
"""convert Roman numeral to integer"""
result = 0
index = 0
for numeral , integer in roman_numeral_map:
while s[index:index+ len (numeral )] == numeral:
result + = integer
index + = len (numeral )
print ('found' , numeral , 'of length' , len (numeral ), ', adding' , integer )
>>> import roman5
>>> roman5. from_roman ('MCMLXXII' )
found M , of length 1, adding 1000
found CM of length 2, adding 900
found L of length 1, adding 50
found X of length 1, adding 10
found X of length 1, adding 10
found I of length 1, adding 1
found I of length 1, adding 1
1972
Перезапустим тесты :
you @ localhost: ~/diveintopython3/examples$ python3 romantest5. py
.......
----------------------------------------------------------------------

166

Ran 7 tests in 0.060s
OK

У меня есть для вас д_ghости. Обе хорошие. Во -перuonmgdpby
from_roman() работает для праbevgh]hh^Z ihdjZcg_cf_j_^ey
из_klguoagZq_gbc о -вторых, наш "круг" замкнулся. Эти дZnZdlZ
позheyxlам быть у_j_gguf\lhfqlhnmgdpbbWRBURPDQ( ) и from_roman()
работают праbevgh^eyсех корректных значений. (На самом деле,
праbevghklvjZ[hlug_]ZjZglbjh\ZgZL_hj_lbq_kdbnmgdpbyWRBURPDQ
может иметь баг в реализации, из -за которого получается непраbevgh_
предстаe_gb_qbkeZ\jbfkdhcnhjf е для некоторых входных данных, а
функция from_roman() может иметь "обратный" баг, из -за которого результатом
uiheg_gbyyляется число, по счастлиhckemqZcghklbkhпадающее с
исходным. Если вас это беспокоит, напишите более сложные тесты.)
Больше плохих "вводов"
Now that the from_roman() function works properly with good input, it's time to fit in
the last piece of the puzzle: making it work properly with bad input. That means
finding a way to look at a string and determine if it's a valid Roman numeral. This is
inherently more difficult than validating numeric input in the to_roman() function, but
you have a powerful tool at your disposal: regular expressions. (If you’re not familiar
with regular expressions, now would be a good time to read the regular e xpressions
chapter.) As you saw in Case Study: Roman Numerals, there are several simple
rules for constructing a Roman numeral, using the letters M, D, C, L, X, V, and I.
Let's review the rules: 1. Sometimes characters are additive. I is 1, II is 2, and II I is
3. VI is 6 (literally, “5 and 1”), VII is 7, and VIII is 8. 2. The tens characters (I, X, C,
and M) can be repeated up to three times. At 4, you need to subtract from the next
highest fives character. You can't represent 4 as IIII; instead, it is repr esented as IV
(“1 less than 5”). 40 is written as XL (“10 less than 50”), 41 as XLI, 42 as XLII, 43 as
XLIII, and then 44 as XLIV (“10 less than 50, then 1 less than 5”). 3. Sometimes
characters are… the opposite of additive. By putting certain characters before
others, you subtract from the final value. For example, at 9, you need to subtract
from the next highest tens character: 8 is VIII, but 9 is IX (“1 less than 10”), not VIIII
(since the I character can not be repeated four times). 90 is XC, 900 is CM . 4. The
fives characters can not be repeated. 10 is always represented as X, never as VV.
100 is always C, never LL. 5. Roman numerals are read left to right, so the order of
characters matters very much. DC is 600; CD is a completely different number (40 0,
“100 less than 500”). CI is 101; IC is not even a valid Roman numeral (because you
can't subtract 1 directly from 100; you would need to write it as XCIX, “10 less than
100, then 1 less than 10”). Thus, one useful test would be to ensure that the
from_r oman() function should fail when you pass it a string with too many repeated
numerals. How many is “too many” depends on the numeral.

167

class FromRomanBadInput (unittest .TestCase ):
def test_too_many_repeated_numerals (self ):
'''from_roman should fail with too many repeated numerals'''
for s in ('MMMM' , 'DD' , 'CCCC' , 'LL' , 'XXXX' , 'VV' , 'IIII' ):
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. from_roman , s)
Another useful test would be to check that certain patterns aren’t repeated. For
example, IX is 9, but IXIX is never valid.
def test_repeated_pairs (self ):
'''from_roman should fail with repeated pairs of numerals'''
for s in ('CMCM' , 'CDCD' , 'XCXC' , 'XLXL' , 'IXIX' , 'IVIV' ):
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. from_roman , s)
A third test could check that numerals appear in the correct order, from highest to
lowest value. For example, CL is 150, but LC is never valid, because the numeral for
50 can never come before the numeral fo r 100. This test includes a randomly
chosen set of invalid antecedents: I before M, V before X, and so on.
def test_malformed_antecedents (self ):
'''from_roman should fail with malformed antecedents'''
for s in ('IIMXCC' , 'VX' , 'DCM' , 'CMM' , 'IXIV' , 'MCMC' , 'XCX' , 'IVI' , 'LM' , 'LD' , 'LC' ):
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. from_roman , s)
Each of these tests relies the from_roman() function raising a new exception,
InvalidRomanNumeralError, which we haven’t defined yet.
def test_malformed_antecedents (self ):
# roman6.py
class InvalidRomanNumeralError (ValueError ): pass
All three of these tests should fail, since the from_roman() function doesn’t currently
have any validity checking. (If they don’t fail now, then what the heck are they
testing?)
you @ localhost: ~/diveintopython3/examples$ python3 romantest6. py
FFF.......
================================================================
FAIL: test_malformed_antecedents (__main__ .FromRomanBadInput )
from_roman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest6.py" , line 113 , in test_malformed_antecedents
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. from_roman , s)
AssertionError : InvalidRomanNumeralError not raised by from_roman
================================================================
FAIL: test_repeated_pairs (__main__ .FromRomanBadInput )

168

from_roman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest6.py" , line 107 , in test_repeated_pairs
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. from_roman , s)
AssertionError : InvalidRomanNumeralError not raised by from_roman
================================================================
FAIL: test_too_many_repeated_numerals (__main__ .FromRomanBadInput )
from_roman should fail with too many repeated numerals
----------------- -----------------------------------------------------
Traceback (most recent call last ):
File "romantest6.py" , line 102 , in test_too_many_repeated_numerals
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. from_roman , s)
AssertionError : InvalidRomanNumeralError not raised by from_roman
----------------------------------------------------------------------
Ran 10 tests in 0.058s
FAILED (failures =3)
Good deal. Now, all we need to do is add the regular expression to test for valid
Roman nu merals into the from_roman() function.
CD
И опять тестируем ...
you @ localhost: ~/diveintopython3/examples$ python3 romantest7. py
..........
----------------------------------------------------------------------
Ran 10 tests in 0.066s
OK
И премия за главное разочароZgb_ этом году достается... я так hegmxkv
слову "ОК", u\_^_gghfmdZdj_amevlZlmki_rgh]hijhoh`^_gbyl_klZ.

169

Рефакторинг

Погружение
Нраblky<Zfbebg_lgh[Z]bkemqZxlkyG_kfhljygZсе усилия при
создании полных модульных тестов, баги всё раghkms_klуют. Что я
подразумеZxih^keh\hf[Z]";Z] - это тестоuckemqZcdhlhjuc_szg_
написан.
>>> import roman7
>>> roman7. from_roman ('' ) ①
0
① Собст_ggh[Z]<uah from_roman с параметром пустой строки (или любой
другой последоZl_evghklbkbfолоg_yляющейся праbevgufjbfkdbf
числом) должен за_jrblvkybkdexq_gb_f,QYDOLG5RPDQ1XPHUDO(UURU.
После hkijhbaедения бага и до его фикса ции следует написать тестоuc
случай за_jrZxsbckyhrb[dhclZdbfh[jZahfbeexkljbjmy[Z].
class FromRomanBadInput (unittest .TestCase ):
.
.
.
def testBlank (self ):
'''from_roman should fail with blank string'''
self .assertRaises (roman6. InvalidRomanNumeralError , roman6. f
rom_roman , '' ) ①
① Всё предельно просто: uauаем from_roman() с пустой строкой и
про_jy_fqlhыбрасыZ_lkybkdexq_gb_,QYDOLG5RPDQ1XPHUDO(UURUKZfh_
сложное было найти баг; теперь, когда из_klghqlhlZdZyhrb[dZkms_klует,
кодироZgb_ijh\_jdbg_aZcfzlfgh]hремен и.
Так как код содержит баг и мы имеет тест про_jdbwlh]h[Z]Zlh^Zgguc
тестоuckemqZcaZершается ошибкой:

170

you @localhost:~ /diveintopython3 /examples$ python3 romantest8.py -v
from_roman should fail with blank string ... FAIL
from_roman should fail with malformed antecedents ... ok
from_roman should fail with repeated pairs of numerals ... ok
from_roman should fail with too many repeated numerals ... ok
from_roman should give known result with known input ... ok
to_roman should give known result with known input ... ok
from_roman (to_roman (n)) ==n for all n ... ok
to_roman should fail with negative input ... ok
to_roman should fail with non -integer input ... ok
to_roman should fail with large input ... ok
to_roman should fa il with 0 input ... ok

===================================================================
FAIL: from_roman should fail with blank string
-------------------------------------------------------------------
Traceback (most recent call last ):
File "romantest8.py" , line 117 , in test_blank
self.assertRaises (roman8.InvalidRomanNumeralError, roman8.from_
roman, '' )
AssertionError: InvalidRomanNumeralError not raised by from_roman

-------------------------------------------------------------------
Ran 11 tests in 0.171s

FAILED (failures =1)
Только теперь Вы можете испраeylv[Z].
def from_roman (s):
'''convert Roman numeral to integer'''

171

if not s:

raise InvalidRomanNumeralError ('Input can not be blank' )
if not re .search (romanNumeralPattern , s):
raise InvalidRomanNumeralError ('Invalid Roman numeral: {}' .
format (s)) ②

result = 0
index = 0
for numeral , integer in romanNumeralMap:
while s[index:index+ len (numeral )] == numeral:
result + = integer
index + = len (numeral )
return result
① Требуется всего 2 строчки кода: яgZyijhерка с пустой строкой и выброс
исключения.
② Не у_j_gmihfbgZehkvebjZg__ книге, поэтому пусть это будет
последний трюк при форматироZgbbkljhdGZqbgZyk3\WKRQjZaj_rZ_lky
опускать числа при использоZgbbbg^_dkh\ihabpbb\kljhd_
форматироZgbyLh_klvместо использоZgby^`^eykku лки на первый
параметр метода format(), Вы можете писать {} и Python заполнит
соответсmxsbbcbg^_dkihabpbbaZ<ZkWlhыполняется для любого
количестZZj]mf_glh\i_j\u_kdh[db^`jZ\ghkbevgu^`\lhju_kdh[db^`
раghkbevgu^`blZd^Ze__.
you @local host:~ /diveintopython3 /examples$ python3 romantest8.py -v
from_roman should fail with blank string 

from_roman should fail with malformed antecedents ... ok
from_roman should fail with repeated pairs of numerals ... ok
from_roman should fail with too many repeated numerals ... ok
from_roman should give known result with known input ... ok
to_roman should give known result with known input ... ok
from_roman (to_roman (n)) ==n for all n ... ok
to_roman should fail with negative input ... ok

172

to_roman should fail with non -integer input ... ok
to_roman should fail with large input ... ok
to_roman should fail with 0 input ... ok

-------------------------------------------------------------------
Ran 11 tests in 0.156s

OK ②
① Тест на обработку пустой строки теперь проходит, значит баг исправлен.
② Все остальные тестоu_kemqZbihij_`g_fmыполняются без ошибок, а
это значит, что при испраe_gbbhrb[dbg_fug_^h[Zили ноuoKZfh_
j_fyqlh[uhklZghиться праblvdh^!
КодироZgb_q_j_agZibkZgb_l| стов не облегчает процесс испраe_gby
баго>eybkijZ\e_gbyijhkluohrb[hd dZd\ijbедённом примере)
необходимы простые тесты; сложные ошибки, конечно же, требуют сложных
тестов. Если _^zlkyjZajZ[hldZijh_dlZq_j_al_klbjhание, то может
показаться , что фиксация бага займёт больше j_f_gblZddZd<Zfijb^zlky
найти строчки кода с багом (собственно, написать тестоuckemqZc^ey
про_jdbwlbokljhq_d baZl_fbkijZить баг. Если тест опять за_jrZ_lkyg_
успешно, то придётся разобраться верно ли был испраe_g[Z]bebkZfl_kl
содержит ошибки. Тем не менее, при длительной разработке эти испраe_gby
dh^| -l_kl| -dh^_hdmiZxlk_[ylZddZdkdhj__\k_]h[Z][m^_lbkijZлен с
перh]hjZaZLZd`_lZddZd<ufh`_l_e_]dhi_j_aZimkdZlv k_ тесты,
dexqZy ноuc<ugZ\jy^ebbkihjlbl_klZjucdh^ijbnbdkZpbb[Z]Z
Сегодняшние модульные тесты завтра преjZlylky\l_kluj_]j_kkbb.
Управляемся с изменением требований
Рефакторинг
Выводы

173

Файлы

11. Файлы

«деylvfbevoh^v[uwlhg_rmldZhkh[_ggh\hремя дождя.» — Harry
Kemelman, The Nine Mile Walk
На моём Windows ноутбуке было 38493 файла, прежде чем я устаноbeh^gh
приложение. УстаноdZ3\WKRQ^h[Zила почти 3000 файлов к общему
объёму. Файлы предстаeyxlkh[hci_jичную парадигму хранения
информации в осноguohi_jZpbhgguokbkl_fZowlZdhgp_ipbygZklhevdh
укоренилась, что большинство людей не hkijbfmlg_qlh^jm]h_
альтернативное. Образно гоhjy<Zrdhfivxl_jlhg_l\fhj_nZceh\.
11.2 Чтение из текстовых файлов
Прежде чем читать из текстоh]hnZceZ его требуется открыть. Открытие
файла 3\WKRQe_]dhыполнить:
a_file = open('examples/chinese.txt', encoding='utf -8')
Python имеет встроенную функцию open(), которой передается имя файла 
качест_Zj]mf_glZ<ijbf_j_bfynZceZ
H[DPSOHVFKLQHVHW[W
b в нём есть 5
интересных _s_c:
1. Это не просто имя файла, это комбинация пути к каталогу и имя файла.
Гипотетически, nmgdpbxhldjulbynZceZfh`gh[ueh[ui_j_^Zlv^\Z
параметра: путь к файлу и имя файла, но nmgdpbxRSHQ fh`gh
передать только один. В Py thon, когда это необходимо ufh`_l_
dexqZlv\k_bebg_dhlhju_imlbddZlZeh]m.
2. При указании пути к каталогу используется / (прямая обратная черта,
слэш, праuckewr g_h[km`^ZydZdZyhi_jZpbhggZykbkl_fZ
используется. Windows использует \ (обратную кос ую черту, обратный
слэш, слэш e_о) для указания пути к каталогам, а операционные
системы Linux и MacOS используют / (прямая обратная черта, слэш,
праuckewr <3\WKRQijyfhckewrijhklhjZ[hlZ_l\k_]^Z^Z`_gZ
Windows.

174

3. Путь каталога не начинается с ко сой черты (слэша) или буквы, это
назыZ_lkyhlghkbl_evgufiml_fHlghkbl_evghq_]h"Bf_cl_ji_gb_
кузнечик!
4. Это строки. Все соj_f_ggu_hi_jZpbhggu_kbkl_fu ключая Windows)
используют Unicode для хранения имён файлов и директорий. Python 3
полностью под держиZ_lg| -ascii пути.
5. Файл не обязательно должен находиться на локальных дисках. Вы
можете использовать сетеu_^bkdbWlhlnZcefh`_l[ulvh[t_dlhf
bjlmZevghcnZceh\hckbkl_fu SURF\OLQX[ ?keb\Zrdhfivxl_j
считает это за файл и даёт hafh`ghklv обращаться к этому как к файлу,
то Python сможет открыть этот файл.
Вызов функции open() не ограничиZ_lkyi_j_^Zq_ciZjZf_ljZimlbdnZcemb
его имени. Имеется ещё один параметр, назыZxsbckyHQFRGLQJH^Z
дорогой читатель, это зmqblоистину ужасно!
11.2.1 Особенности кодировки показывают своё страшное лицо
Байты байт; симheuZ[kljZdpbbKljhdZij_^klZляет собой
последоZl_evghklvkbfоло\dh^bjhке Юникод. Но файлы на диске не
яeyxlkyihke_^hательностью симheh dh^bjhке Unicode, а яeyxlky
последоZl_evghklvx[Zclh. Если uqblZ_l_l_dklhый файл с диска, то как
Python преобразует эту последоZl_evghklv[Zcl\ihke_^hательность
симheh? Он декодирует байт по определенному алгоритму кодироdbb
haращает последовательность симheh dh^bj оd_8QLFRGH l. е. в виде
строки).
>> file = open ('examples/chinese.txt' )
…>>> a_string = file .read ()
…Traceback (most recent call last ):
… File « », line 1, in
… File «C: \Python31 \lib \encodings \cp1252. py », line 23 , in decode
… return codecs .charmap_decode (input , self .errors , decoding_table )[
0]
…UnicodeDecodeError : 'charmap' codec can’t decode byte 0x8f in posi
tion 28 : character maps to

[[ Категория :Погружение в Python 3]]

175

XML

Погружение
Большинстh]eZ\ этой книге строятся на отрыdZoijbf_jZodh^ZGh[PO
это больше данные, нежели код. Один из способоijbf_g_gby[POwlh
«синдикация контента» такого, как последние статьи с блога, форума или
других часто обноey_fuokZclh\;hevrbgklо популярного ПО для _^_gby
блогоfh`_lkha^Zать ленты (фиды) и обноeylvbodh]^Zgh\u_klZlvb
темы публикуются. Вы можете следить за блогом подписаrbkvgZ_]hdZgZe
также ufh`_l_ke_^blvaZg_kdhevdbfb[eh]Zfbijbihfhsbijh]jZff -
агрегатороlw ких, как Google Reader [1]
Итак, ниже предстаe_gu;0/^Zggu_kdhlhjufbfu[m^_fjZ[hlZlv\wlhc
гла_Wlh фид формата Atom syndication feed


dive into mark
currently between addictions
tag:diveintomark.org,2001 -07 -29:/
2009 -03 -27T21:56:07Z




Mark
http://diveintomark.org/

Dive into history, 2009 edition

176

href ='http://diveintomark.org/archives/2009/03/27/dive -into -h
istory -2009 -edition' />
tag:diveintomark.org,2009 -03 -27:/archives/20090327172042 d>
2009 -03 -27T21:56:07Z
2009 -03 -27T17:20:42Z



Putting an entire chapter on one page sounds
bloated, but consider this & mdash; my longest chapter so fa
r
would be 75 printed pages, and it loads in under 5 seconds &
hellip;
On dialup.




Mark
http://diveintomark.org/

Accessibility is a harsh mistress
href ='http://diveintomark.org/archives/2009/03/21/accessibili
ty -is -a-harsh -mistress' />
tag:diveintomark.org,2009 -03 -21:/archives/20090321200928 d>
2009 -03 -22T01:05:37Z
2009 -03 -21T20:09:28Z
/>

177

The accessibility orthodoxy does not permi
t people to
question the value of features that are rarely useful and rar
ely used.




Mark

A gentle introduction to video encoding, part 1: contain <br /> er formats
href ='http://diveintomark.org/archives/2008/12/18/give -part -1
-container -formats' />
tag:diveintomark.org,2008 -12 -18:/archives/20081218155422 d>
2009 -01 -11T19:39:22Z
2008 -12 -18T15:54:22Z








These notes will eventually become part of
a
tech talk on video encodin g.




178

5-минутное введение в XML
Если Вы уже знакомы с XML, то можете пропустить эту глаm.
XML — это язык разметки для описания иерархии структурироZgguo^Zgguo
XML документ содержит один или более элементов разделённых
открыZxsbfb и закрыZxsbfbl_]Zfb . Это праbevgucohlyb
неинтересный, XML документ:


① Это открыZxsbc gZqZevguc) тег элемента foo.
② Это соот_lkl\mxsbc закрыZxsbc dhg_qguc) тег элемента foo. Как 
математике и языках программироZgbydZ`^Zyhldjuающая скобка должна
иметь соот_lklующую закрыZxsmx XML каждый открыZxsbcl_]
должен быть закрыт соот_lklующим закрыZxsbf.
Элементы могут быть неограниченно eh`_gu друг в друга. Так как элемент
bar eh`_g\we_f_glIRRlh_]hgZauают подэлементом или дочерним
элементом элемента foo.



Первый элемент каждого XML документа назыZ_lkydhjg_ым. XML документ
может содержать только один корнеhcwe_f_g т. Пример предстаe_gguc
ниже не яey_lky;0/^hdmf_glhf , так как он имеет дZdhjg_\uowe_f_glZ:


Элементы могут иметь атрибуты состоящие из пары имя -значение. Атрибуты
перечисляются gmljbhldjuающего тега элемента и разделяются
пробелами. [wаp-rоbin .com ] Имена атрибутов не могут повторяться gmljb
одно элемента. Значения атрибутов должны быть обрамлены одинарными
или дhcgufbdZ\uqdZfb.




179

① Элемент foo имеет один атрибут именоZggucdZdODQJAgZq_gbxZljb[mlZ
lang присваиZ_lkykljhdZHQ.
② Элемент bar имеет дZZljb[mlZLGbODQJAgZq_gb_ODQJ_klvIUWlhg_
приh^blddhgnebdlmkZljb[mlhfODQJwe_f_glZIRRlZddZddZ`^ucwe_f_gl
имее т сhcgZ[hjZljb[mlh.
Если элемент имеет больше чем один атрибут, то порядок атрибутоg_b]jZ_l
роли. Атрибуты элементов есть неупорядоченный набор ключей и значений
подобно словарям 3\WKRQ>eydZ`^h]hwe_f_glZfh`ghmdZaZlv
неограниченное число атри буто.
Элементы могут иметь текст (текстоh_kh^_j`Zgb_) .

PapayaWhip

Элементы которые не содержат текста и дочерних элементов назыZxlky
пустыми .

Сущестm_lkhdjZszggZyaZibkvimklh]hwe_f_glZIhf_klb\agZd^jh[b
конце открывающего тега, ufh`_l_ijhimklblvaZdjuающий тег. XML
документ предыдущего примера с пустым элементов может быть записан
следующим образом:

Подобно тому как функции Python могут быть объяe_gu\jZaguo модулях ,
XML элементы могут быть объяe_gu разных пространстZobfzg
(namespaces) . ПространстZbfzgh[uqghыглядят как URL -пути. Для
объяe_gby пространстZbfzgihmfheqZgbx используется директиZ[POQV
Объяe_gb е пространстZbfzghq_gvihoh`_gZZljb[mlghbf__l
специальное значение.

dive into mark

① Элемент feed находится ijhkljZgklе имён http://www.w3.org/2005/Atom .
② Элемент title также находится в пространстве имён
http://www.w3.org/2005/Atom . Пространстhbfzgijbf_gy_lkydZddwe_f_glm
котором оно б ыло определено так и ко k_f^hq_jgbfwe_f_glZf.

180

Вы можете объявлять пространстhbfzg[POQVSUHIL[bklZить ему 
соответстb_ префикс prefix. Тогда каждый элемент в данном пространст_
имён должен быть яghh[tyлен с указанием префикса prefix.

dive into mark

① Элемент feed находится ijhkljZgklе имён http://www.w3.org/2005/Atom .
② Элемент title также находится в пространстве имён
http://www.w3.org/2005/Atom .
С точки зрения синтаксического анализатора XML, предыдущие дZ;0/
документа идентичны. Пара «пространстhbfzgbfywe_f_glZaw дают
XML идентичность. Префиксы используются только для ссылки на
пространстhbfzgghg_baf_gyxlbf_gbZljb[mlZ?kebijhkljZgklа имён
соiZ^Zxlbf_gZwe_f_glh\khпадают, атрибуты (или их отсутстb_
соiZ^Zxlbl_dkluwe_f_glh\khпадают, то XML док ументы одинаковы.
И, наконец, XML документы могут содержать информацию о кодироd_
симheh в перhckljhd_^hdhjg_ого элемента. (Если Вам интересно как
документ может содержать информацию которая должна быть из_klgZ;0L -
анализатору до анализа XML докум ента, то смотрите Catch -22 раздел F XML
спецификации )

Теперь Вы знаете об XML достаточно чтобы «ug_klbke_^mxsb_jZa^_eu
глаu!
Структура ф ормата синдикации фида Atom
Рассмотрим блог (weblog) или любой сайт с часто обноey_fufdhgl_glhf
например CNN.com . Сайт содержит заголоhd &11FRP ih^aZ]heh\hd
(«Breaking News, U.S., World, Weather, Entertainment & Video News»), дату
последнего изменения («обноe_ghSP('76DW0D\ b
список статей опубликованных jZagh_ремя. Каждая статья в сhxhq_j_^v
также имеет заголоhd^Zlmi_jой публикации (и, hafh`gh^Zlmihke_^g_]h
обноe_gby\k лучае если статья была корректироZgZ bmgbdZevguc85/.
Формат синдикации Atom разработан с целью хранить информацию подобного
рода стандартным образом. Мой блог и CNN.com абсолютно разные по
дизайну, содержанию и посетителям сайты, но оба имеют сходную с труктуру.
Оба сайта имеют заголоdbbim[ebdmxlklZlvb.

181

На _jog_fmjhне фид Atom должен иметь корнеhcwe_f_glihbf_gbIHHG
находящийся ijhkljZgklе имен http://www.w3.org/2005/Atom .
xml:lang ='en' > ②
① http://www.w3.org/2005/Atom - пространстh имён Atom
② Каждый элемент может содержать атрибут xml:lang который определяет
язык элемента и его дочерних элементов. В данном случае атрибут xml:lang
объяe_gguc\dhjg_ом элементе задаёт английский язык для всего фида.
Фид Atom содержит дополнительную информацию о себе ^hq_jgbo
элементах корневого элемента:

dive into mark

currently between addictions

tag:diveintomark.org,2001 -07 -29:/

2009 -03 -27T21:56:07Z


① Заголоhd title содержит текст 'dive into mark'.
② Подзаголоhd subtitle фида есть строка 'currently between addictions'.
③ Каждый фид должен иметь глобальный уникальный идентификатор. RFC
4151 содержит информацию как создавать такие идентификаторы.
④ Данный фид был обноezgihke_^gbcjZafZjlZ 21:56 GMT.
Обычно элемент updated экbалентен дате последнего изменения какой -либо
из статей на сайте.
⑤ А hla^_kvgZqbgZ_lkykZfh_bgl_j_kgh_We_f_glkkuedbOLQNg_bf__l
текстоh]hkh^_j`Zgbyghbf__lljbZljbx ута: rel, type и href. Значение
атрибута rel гоhjblhlhfdZdh]hlbiZkkuedZUHO
DOWHUQDWH
agZqblqlhwlh
альтернативная ссылка этого фида. Атрибут type='text/html' гоhjblqlhwlh
ссылка на HTML страницу. И, собственно, путь ссылки содержится в атри буте
href.

182

Теперь мы знаем, что предстаe_ggucыше фид получен с сайта «dive into
mark». Сайт доступен по адресу http://diveintomark.org/ и последний раз был
обноezgfZjlZ.

Хотя в некоторых XML документах порядок элементов может иметь
значение, в фидах Atom порядок элементов J=произhevguc. =
Продолжим дальше рассматриZlvkljh_gb_nb^Zihke_f_lZbgnhjfZpbbh
фиде идёт список последних статей. Статья u]ey^blke_^mxsbfh[jZahf:



Mark
http://diveintomark.org/

Dive into history, 2009 edition


href ='http://diveintomark.org/archives/2009/03/27/dive -into -his
tory -2009 -edition' />
tag:diveintomark.org,2009 -03 -27:/archives/20090327172042

2009 -03 -27T21:56:07Z

2009 -03 -27T17:20:42Z
> ⑥


Putting an entire chapter on one page sounds

bloated, but consider this & mdash; my longest chapter so fa
r
would be 75 printed pages, and it loads in under 5 seconds &
hellip;
On dialup.


183



① Элемент author сообщает о том, кто написал статью: некоторый парень по
имени Марк (Mark), который Zey_l^mjZdZgZkZcl_ http://diveintomark.org/ (В
данном случае ссылка на сайт аlhjZkhпадает с альтернативной ссылкой 
метаинформации о фиде, но это не k_]^ZijZда, так как многие блоги имеют
несколько авторов, у каждого из которых — сhckZcl)
② Элемент title содержит заголоhdklZlvb'LYHLQWRKLVWRU\HGLWLRQ.
③ Как и с альтернатиghckkuedhcgZnb^ элементе link находится адрес
HTML _jkbb^ZgghcklZlvb.
④ Элемент entry, подобно фидам, имеет уникальный идентификатор.
⑤ Элемент entry имеет д_^Zlu^Zlmi_jой публикации и дату последнего
изменения.
⑥ Элементы entry могут иметь произhevgh_dhebq_klо категорий category.
РассматриZ_fZyklZlvyihiZ^zl категории diveintopython, docbook и html.
⑦ Элемент summary даёт кра ткий обзор статьи. (Бывает также не
предстаe_gguca^_kvwe_f_glkh^_j`ZgbyFRQWHQWij_^gZagZq_gguc^ey
dexq_gby фид полного текста статьи.) Данный элемент summary содержит
специфичный для фидов Atom атрибут type='html' указыZxsbcqlh
содержимое элемен та есть текст nhjfZl_+70/Wlhажно, так как HTML -
объекты — и … присутстmxsb_ элементе должны отображаться
как « — » и «…», а не печататься «как есть».
⑧ И, наконец, закрыZxsbcl_]we_f_glZHQWU\]hорит о конце метаданных
для этой стать и.
Синтаксический разбор XML
В Python документы XML могут быть обработаны c использоZgb_fjZaguo
библиотек. Язык имеет обычные синтаксические анализаторы DOM и SAX , но
я буду использоZlv^jm]mx[b[ebhl_dm(OHPHQW7UHH.
>>> import xml .etree .ElementTree as etree ①
>>> tree = etree. parse ('examples/feed.xml' ) ②
>>> root = tree. getroot () ③
>>> root ④


184

① Модуль ElementTree oh^bl в стандартную библиотеку Python, путь для
импорта xml.etree.ElementTree.
② Функция parse() — это базоZynmgdpbyfh^mey(OHPHQW7UHHNmgdpby
принимает имя файла или файлоподобный объект. Эта функция uihegy_l
синтаксическй анализ документа за раз. Если разрабатыZ_fZyi рограмма
должна экономить память, то можно анализироZlv;0/^hdmf_glqZklyfb.
③ Функция parse() hajZsZ_lh[t_dldhlhjucyляется предстаe_gb_f
k_]h^hdmf_glZH^gZdhh[t_dlWUHHg_yляется корнеufwe_f_glhfQlh[u
получить ссылку на корнеhcwe_f_gl , необходимо uaать метод getroot().
④ Как и следоZehh`b^Zlvdhjg_\hcwe_f_gl_klvwe_f_glnb^Z\
пространст_bfzgKWWSZZZZRUJ$WRPKljhdh\h_ij_^klZление
объекта root ещё раз подчёркиZ_lажный момент: XML элемент — это
комбинация прост ранстZbfzgb_]hbf_gb -тега (так же назыZ_fh]h
локальным именем). Каждый элемент ^Zgghf^hdmf_gl_gZoh^blky
пространст_$WRPihwlhfmdhjg_\hcwe_f_glij_^klZлен как
{http://www.w3.org/2005/Atom}feed.

Модуль ElementTree k_]^Zij_^klZляет элеме нты XML как
'{пространстhbfzg`ehdZevgh_bfy
<Zfg_h^ghdjZlghij_^klhbl
использоZlvwlhlnhjfZlijbbkihevahании API ElementTree. =
=
Элементы XML есть списки Python
В API ElementTree элементы предстаeyxlky\kljh_gguflbihf3\WKRQ -
списком. Каждый из элементоkibkdZij_^klZляет собой дочерние XML
элементы.
# продолжение предыдущего примера
>>> root .tag ①
'{ http :// www .w3. org /2005/ Atom }feed '
>>> len (root ) ②
8
>>> for child in root: ③
... print (child ) ④
...





185





① Продолжим предыдущий пример: корнеhcwe_f_glURRW -
{http://www.w3.org/2005/Atom}feed
② "Длина" корневого элемента есть количестh^hq_jgbowe_f_glh\URRW.
③ Вы можете использоZlvwe_f_gldZdbl_jZlhjihсем дочерним
элементам.
④ Из сообщений b^ghql о в элементе root 8 дочерних элементо
элементоkf_lZbgnhjfZpb_chnb^_ WLWOHVXEWLWOHLGXSGDWHGbOLQN b
элемента со статьями entry.
Вы, должно быть, уже догадались, но я хочу яghmdZaZlvgZke_^mxs__
список дочерних элементов содержит толь ко прямые дочерние элементы. В
сhxhq_j_^vdZ`^uc^hq_jgbcwe_f_glHQWU\fh`_lkh^_j`Zlvkои дочерние
элементы, но они не будут dexq_gu список. Они будут dexq_gu список
элемента entry, а не в список подэлементов элемента feed. Найти
определённые эл ементы любого уроgyложенности можно несколькими
способами; ниже мы рассмотрим 2 из них.
Атрибуты XML есть словари Python
Напомним, что документ XML это не только набор элементов; каждый элемент
так же имеет набор атрибутоBf_ydhgdj_lguc;0/we_f_gl Вы можете
легко получить его атрибуты как слоZjv3\WKRQ.
# продолжение предыдущего примера
>>> root. attrib ①
{'{http://www.w3.org/XML/1998/namespace}lang' : 'en' }
>>> root [4] ②

>>> root [4].attrib ③
{'href' : 'http://diveintomark.org/' ,
'type' : 'text/html' ,
'rel' : 'alternate' }
>>> root [3] ④


186

>>> root [3].attrib ⑤
{}
① Сhckl\hDWWULEозращает словарь атрибутоwe_f_glZBkoh^gZyjZaf_ldZ
XML была следующая .
Префикс xml: ссылается на стандартное пространстhbfzgd оторое любой
XML документ может использоZlv[_ah[tyления.
② Пятый подэлемент есть элемент link (используется индекс [4], так как
списки Python индексируются начиная с 0).
③ Подэлемент link имеет три атрибута href, type и rel.
④ Четвёртый подэлемент (с и ндексом [3] в списке начинающемся с 0) — это
элемент updated.
⑤ Подэлемент updated не имеет атрибутоke_^hательно сhcklо .attrib
hajZsZ_limklhckeh\Zjv.
Поиск узлов в XML документе
До настоящего момента мы рассматриZeb;0/^hdmf_glkерху gba
начиная с корнеh]hwe_f_glZ^Ze__d_]h^hq_jgbfwe_f_glublZd\]em[v
k_]h^hdmf_glZH^gZdhо многих случаях при работе с XML Вам необходимо
искать конкретные элементы. Etree спраblk я и с этой задачей .
>>> import xml .etree .ElementTree as etree
>>> tree = etree. parse ('examples/feed.xml' )
>>> root = tree. getroot ()
>>> root. findall ('{http://www.w3.org/2005/Atom}entry' ) ①
[,
,
]
>>> root. tag
'{http://www.w3.org/2005/Atom}feed'
>>> root. findall ('{http://www.w3.org/2005/Atom}feed' ) ②
[]
>>> root. findall ('{http://www.w3.org/2005/Atom}author' ) ③
[]

187

① Метод findall() uihegy_lihbkd^hq_jgbowe_f_glh удовлетhjyxsbo
запросу. (Формат запроса рассматриZ_lkygb`_)
② Все элементы (dexqZydhjg_ой и дочерние) имеют метод findall(). Метод
находит k_we_f_glukj_^b^hq_jgbokhhlетстmxsb_aZijhkmIhq_fm`_
метод _jgmeimklhckibkhd"Ohlywlhfh`_lihdZaZlvkyg_hq_идным, данный
запрос ищет только ^hq_jgbowe_f_glw х. Так как корнеhcwe_f_glIHHGg_
имеет дочерних элементов по имени feed, то запрос возjZsZ_limklhc
список.
③ Этот результат также может Вас удиblv<^hdmf_gl_;0/^_cklительно
есть элемент author; на самом деле, их даже три (по одному dZ`^hf
элем енте entry). Но эти элементы author не яeyxlky прямыми
подэлементами (direct children) корнеh]hwe_f_glZhgb —
«подподэлементы» (подэлементы подэлемента). Если Вам нужно найти
элементы author любого уроgyложенности, то придётся изменить строку
запрос а.
>>> tree. findall ('{http://www.w3.org/2005/Atom}entry' ) ①
[,
,
]
>>> tree. findall ('{http://www.w3.org/2005/Atom}author' ) ②
[]
① Для удобстZh[t_dlWUHH dhlhjucозjZsZ_lnmgdpbyHWUHHSDUVH bf__l
несколько методоb^_glbqguof_lh^Zfdhjg_ого элемента. Результаты
функции такие же как при uahе метода tree.getroot().findall().
② На_jgh_m^bлены, однако этот запрос не находит элемента author 
данном документе. Почему же? Потому что, этот вызов идентичен uahу
tree.getroot().findall('{http://www.w3.org/2005/Atom}author'), что значит «найти k_
элементы author, которые яeyxlky подэлементами корнеh]hwe_f_glZ
Элементы author не яeyxlky^hq_jgbfb^eydhjg_\h]hwe_f_glZhgb
подэлементы элементов entry. Таким образом, при uiheg_gbbaZijhkZ
соiZ^_gbcg_gZc^_gh.
Помимо метода findall() есть метод find() который ha\jZsZ_llhe ько перuc
найденный элемент. Метод может быть полезен в случаях когда j_amevlZl_
поиска Вы ожидаете только один элемент или Вам Z`_glhevdhi_jый
элемент из списка найденных.
>>> entries = tree. findall ('{http://www.w3.org/2005/Atom}entry' )

>>> len (entries )

188

3
>>> title_element = entries [0].find ('{http://www.w3.org/2005/Atom}t
itle' ) ②
>>> title_element. text
'Dive into history, 2009 edition'
>>> foo_element = entries [0].find ('{http://www.w3.org/2005/Atom}foo
') ③
>>> foo_element
>>> type (foo_element )

① Как Вы b^_eb предыдущем примере findall() haращает список
элементоDWRPHQWU\.
② Метод find() принимает запрос ElementTree и ha\jZsZ_li_jый
удовлетhjyxsbcaZijhkmwe_f_gl.
③ Во элементе foo отсутстmxl^hq_jgb_we_f_gluihwlhfmILQG
haращает объект None.

Здесь необходимо отметить закавыку при использоZgbbf_lh^ZILQG <
логическом контексте объекты элементов ElementTree не содержащие
дочерних элементоjZ\guagZq_gbx False (т.е if=len Eelement F=uqbkey_lky
как 0). Код if=element. find E'...D F=про_jy_lg_lhqlhgZrzeebf_lh^ILQG
удовлетhjyxsbcaZijhkmwe_f_gldh^ijhеряет =содержит ли
найденный элемент дочерние элементы! Для того чтобы про_jblvgZrze
ли метод find() элемент необходимо использоZlv if=element. find E'...D F=is =not =
None K=
Рассмотрим поиск gmljb^hq_jgbowe_f_glh, т.е подэлементо
подподэлементов и так далее любого уроgyложенности.
>>> all_links = tree. findall ('//{http://www.w3.org/2005/Atom}link' )

>>> all_links
[,
,
,
]
>>> all_links [0].attrib


189

{'href' : 'http://diveintomark.org/' ,
'type' : 'text/html' ,
'rel' : 'alternate' }
>>> all_links [1].attrib

{'href' : 'http://diveintomark.org/archives/2009/03/27/dive -into -his
tory -2009 -edition' ,
'type' : 'text/html' ,
'rel' : 'alternate' }
>>> all_links [2].attrib
{'href' : 'http:/ /diveintomark.org/archives/2009/03/21/accessibility
-is -a-harsh -mistress' ,
'type' : 'text/html' ,
'rel' : 'alternate' }
>>> all_links [3].attrib
{'href' : 'http://diveintomark.org/archives/2008/12/18/give -part -1-c
ontainer -formats' ,
'type' : 'text/html' ,
'rel' : 'alternate' }
① Этот запрос — //{http://www.w3.org/2005/Atom}link — очень похож на запросы
из предыдущих примероHlebqb_aZdexqZ_lky дmokbfолах косой черты
// gZqZe_kljhdbaZijhkZKbfолы // обозначают «Я хочу найти k_
элементы незаbkbfhhlmjhня eh`_gghkl и, а не только непосредственные
дочерние элементы». Поэтому метод haращает список из четырёх
элементоZg_bah^gh]h.
② Первый элемент результата — прямой подэлемент корневого элемента.
Как мы b^bfba_]hZljb[mlh, это альтернативная ссылка уроgynb да,
которая указыZ_lgZKWPOерсию _[kZclZgZdhlhjhfjZkiheZ]Z_lkynb^.
③ Остальные три элемента результата есть альтернатиgu_kkuedbmjhня
элементоHQWU\DZ`^ucbawe_f_glh entry имеет по одному подэлементу
link. Так как запрос findall() содержал симheu^ойной черты в начале
запроса, то результат поиска содержит все подэлементы link.
В целом, метод findall() библиотеки ElementTree довольно мощный инструмент
поиска, однако формат запроса может быть немного непредсказуем.
Официально формат запросо в ElementTree описан как «ограниченная поддержка
ujZ`_gbc;3DWK» . XPath это стандарт организации W3C для построения

190

запросов поиска внутри XML документа. С одной стороны формат запросо
ElementTree достаточно похож на формат XPath для выполнения простейших
поисков. С другой стороны он отличается настолько, что может начать
раздражать если Вы уже знаете XPath. Далее мы рассмотрим сторонние
библиотеки XML позв оляющие расширить API ElementTree до полной
поддержки стандарта XPath.
Работаем с LXML
lxml это сторонняя библиотека с открытым кодом основанная на из_klghf
синтаксическом анализаторе libxml2 . Библиотека обеспечиZ_lklhijhp_glgmx
соf_klbfhklvk$3,(OHPHQW7UHHiheghklvxih^^_j`bает XPath 1.0 и имеет
несколько других приятных фишек. Для Windows можно скачать инсталлятор ;
пользоZl_eyf/LQX[ke_^m_lijhерить наличие скомпилироZgguoiZd_lh в
репозиториях дистрибутиZ gZijbf_jbkihevamybgkljmf_glu\XPbebDSt -
get). В противном случае придётся устанаeb\ZlvO[PO jmqgmx .
>>> from lxml import etree ①
>>> tree = etree. parse ('examples/feed.xml' ) ②
>>> root = tree. getroot () ③
>>> root. findall ('{http://www.w3.org/2005/Atom}entry' ) ④
[,
,
]
① При импорте lxml предостаey_lZ[khexlghlZdhc`_$3,dZdстроенная
библиотека ElementTree.
② Функция parse(): такая же как (OHPHQW7UHH.
③ Метод getroot(): такой же.
④ Метод findall(): точно такой же.
При обработке больших XML документов lxml значительно быстрее чем
kljh_ggZy[b[ebhl_dZ(OHPHQW7UHH?keb<ubkihevam_l_nmgdpbblhevdhba
API ElementTree и хотите чтобы обработка uihegyeZkvdZdfh`gh[uklj__lh
можно попробоZlvbfihjlbjhать библиотеку lxml и, в случае её отсутстby
использоZlv(OHPHQW7UHH.
tr y:
from lxml import etree
except ImportError :

191

import xml .etree .ElementTree as etree
Однако, lxml не только быстрее чем ElementTree: метод findall() поддержиZ_l
более сложные запросы.
>>> import lxml. etree

>>> tree = lxml. etree .parse ('examples/feed.xml' )
>>> tree. findall ('//{http://www.w3.org/2005/Atom}*[@href]' )

[,
,
,
]
>>> tree. findall ("//{http://www.w3.org/2005/Atom}*[@href='http://di
veintomark.org/']" ) ③
[]
>>> NS = '{http://www.w3.org/2005/Atom}'
>>> tree. findall ('//{NS}author[{NS}uri]' .format (NS =NS ))

[,
]
① В этом примере я импортирую объект lxml.etree (вместо объекта etree: from
lxml import etree ) чтобы подчеркнуть, что описыZ_fu_ hafh`ghklb
реализуемы только с lxml.
② Этот запрос найдёт все элементы ijhkljZgklе имён Atom (любой
eh`_gghklb dhlhju_bf_xlZljb[mlKUHIKbfолы // gZqZe_aZijhkZ
обозначают «элементы любой eh`_gghklbZg_lhevdhihlhfdbdhjg_ого
элемента». { http://www.w3.org/2005/Atom} обозначает «только элементы
пространстZbfzg$WRPKbfол * значит «элементы с любым локальным
именем». И [@href] обозначает «элемент имеет атрибут href».
③ В результате запроса найдены все элементы Atom с атрибутом href раg ым
http://diveintomark.org/.
④ После преобразоZgbykljhdb bgZq_wlbaZijhkuklZghятся неимо_jgh
длинны) данный запрос ищет элементы Atom author имеющие подэлементы
Atom uri. Запрос возjZsZ_llhevdhwe_f_glZDXWKRU перhfbо lhjhf

192

элементах ent ry. В последнем элементе entry элемент author содержит только
имя name, но не uri.
Вам мало? lxml имеет встроенную поддержку для ujZ`_gbc;3DWKFug_
будем детально рассматриZlvkbglZdkbk;3DWKlZddZdwlhl_fZ^ey
отдельной книги. Однако мы рассмо трим пример использоZgby;3DWK lxml.
>>> import lxml. etree
>>> tree = lxml. etree .parse ('examples/feed.xml' )
>>> NSMAP = {'atom' : 'http://www.w3.org/2005/Atom' }

>>> entries = tree. xpath ("//atom:category[@term='accessibility']/..
", ②
... namespaces =NSMAP )
>>> entries

[]
>>> entry = entries [0]
>>> entry. xpath ('./atom:title/text()' , namespaces =NSMAP )

['Accessibility is a harsh mistress' ]
① Чтобы uihegblv;3DWKaZijhkwe_f_glh\baijhkljZgklа имён,
необходимо определить отображение префикса этого пространстZGZkZfhf
деле это обычный словарь Python.
② А hlb;3DWKaZijhk>Zggh_ыражение выполняет поиск элементо
category (пространстZbfzg$WRP kh^_j`Zsb_Zljb[mlkiZjhcbfy -значение
term='accessibility'. Но это не соk_flhqlhозjZsZ_laZijhk<uaZf_lbeb
симheu\dhgp_kljhdbaZijhkZ"Wlhh[hagZqZ_l\_jgbg_gZc^_gguc
элемент, а его ро дителя». И так, одним запросом мы найдём k_we_f_glu
entry с дочерними элементами .
③ Функция xpath() haращает список объекто(OHPHQW7UHH<
анализируемом документе k_]hh^bgwe_f_glHQWU\kZljb[mlhf
term='accessibility '.
④ Выражение XPath не k_]^Zозвращает список элементов. Формально,
DOM разобранного документа XML не содержит элементов, она содержит узлы
(nodes) . В заbkbfhklbhlbolbiZmaeufh]ml[ulvwe_f_glZfbZljb[mlZfb
или даже текстом. Результатом запроса XPath всегда яey_lkykibkhdmaeh\
Этот запрос haращает список текстоuomaeh\l_dklWH[W we_f_glZWLWOH
(atom:title) есть подэлемент текущего элемента (./).

193

Создание XML
ElementTree умеет не только разбирать сущестmxsb_;0/^hdmf_glughb
создават ь их «с нуля».
>>> import xml .etree .ElementTree as etree
>>> new_feed = etree. Element ('{http://www.w3.org/2005/Atom}feed' ,

... attrib ={'{http://www.w3.org/XML/1998/namespace}lang' : 'en' }
) ②
>>> print (etree. tostring (new_feed ))


① Для создания ноh]hwe_f_glZg_h[oh^bfhkha^Zlvh[t_dldeZkkZ(OHPHQW
В качест_i_jого параметра в конструктор мы передаём имя элемента
(пространство имён и локальное имя). Данное ujZ`_gb_kha^Zzlwe_f_gl
feed ijhkljZgklе Atom. Этот будет корнеhcwe_f_glgZr_]hghого
документа XML.
② Для того чтобы добаblvZljb[mludkha^Z\Z_fhfmwe_f_glmfui_j_^Zzf
словар ь имён атрибутоbboagZq_gbc втором аргументе attrib. Заметьте, что
имена атрибуто^he`guaZ^Z\Zlvky формате ElementTree
{пространстhBbfzg`ehdZevgh_Bbfy.
③ В любой момент Вы можете сериализоZlvwe_f_glb_]hih^we_f_glu
используя функцию tostring( ) библиотеки ElementTree.
Вы удиe_guj_amevlZlmk_jbZebaZpbbQHZBIHHG"NhjfZevgh(OHPHQW7UHH
сериализует XML элементы праbevghghg_hilbfZevghIjbf_j;0/
документа в начале глаuhij_^_ezg\ пространст_ihmfheqZgbx
xmlns='http://www.w3.org/2005/Atom '. Определение пространстZihmfheqZgbx
полезно для документов (например, фидов Atom), где k_we_f_glu
принадлежат одному пространстmlh_klv<ufh`_l_h[tyить пространстh
один раз, а на элементы ссылаться используя локальное имя (, ,
). Если Вы не собираетесь объяeylvwe_f_gluba^jm]h]h
пространстZbfzglhg_lg_h[oh^bfhklbbkihevahать префикс пространстZ
по умолчанию.
Синтаксический анализатор XML не «заметит» разницы между документом
XML с пространстhfihmfheqZgbxb^hdmf ентом с использоZgb_fij_nbdkZ
пространстZbfzgi_j_^dZ`^ufwe_f_glhfJ_amevlbjmxsZyfh^_ev'20
данной сериализации u]ey^bldZd


194

что раghagZqgh

Единст_ggZyjZagbpZ\lhfqlh\lhjhcариант на несколько симheh
короче. Если мы переделаем наш пример с использованием префикса ns0: в
каждом открыZxs_fbaZdjuающем тэгах, это добаbeh[ukbfола на
открыZxsbclw]lw]h + 4 симheZgZh[tyy ление собст_ggh
пространстZbfzgсего 320 симheh\<dh^bjhке UTF -8 это состаbeh[u
320 байт. (После архиZpbbJ]LSjZagbpZmf_gvrZ_lky^h[ZclZh^gZdh
байт это 21 байт). Возможно, Вы бы не обратили внимания на эти десятки
байтоgh^eynb^h $WRPdhlhju_aZ]jm`ZxlkylukyqmjZaijbbaf_g_gbb
ub]jurg_kdhevdbo[ZclgZh^ghfaZijhk_[ukljhij_ращается в
килобайты.
Ещё одно преимущестhO[PO отличие от стандартной библиотеки
ElementTree lxml предостаey_l[he__lhgdh_mijZление сериализа цией
элементо.
>>> import lxml. etree
>>> NSMAP = {None : 'http://www.w3.org/2005/Atom' }

>>> new_feed = lxml. etree .Element ('feed' , nsmap =NSMAP )

>>> print (lxml. etree .tounicode (new_feed ))


>>> new_feed. set ('{http://www.w3.org/XML/1998/namespace}lang' , 'en'
) ④
>>> print (lxml. etree .tounicode (new_feed ))

① Для начала определим пространстhbfzg используя словарь. Значения
словаря и есть пространстhbfzgdexqbkeh\Zjy - задаваемый префикс.
Используя объект None dZq_klе префикса мы задаём пространстhbfzgih
умолчанию.
② При создании элемента мы передаём специфичный для lxml аргумент
nsmap, используемый для передачи префиксов пространств имён.
③ Как и ожидали, при сериализации определено пространстhbfzgih
умолчанию Atom и объяe_gh^bgwe_f_glIHHG[_aij_nbdkZijhkljZgklа
имён.

195

④ Опа, мы забыли добаblvZljb[ml[POODQJBkihevamyf_lh^ set(), можно
k_]^Z^h[Zить атрибут к любому элементу. Метод принимает два аргумента:
имя атрибута klZg^ZjlghfnhjfZl_(OHPHQW7UHHbagZq_gb_Zljb[mlZ
(Данный метод есть и [b[ebhl_d_(OHPHQW7UHH?^bgklенное отличие lxml и
ElementTree ^Zgghfijbf| ре это передача аргумента nsmap для указания
префиксоijhkljZgkl\bfzg)
Раз_gZrb^hdmf_gluh]jZgbq_gulhevdhh^gbfwe_f_glhf"Dhg_qghg_l
Мы можем запросто создать дочерние элементы.
>>> title = lxml. etree .SubElement (new_feed , 'title' , ①
... attrib ={'type' :'html' }) ②
>>> print (lxml. etree .tounicode (new_feed )) ③
< title type
='html' />< /feed >
>>> title. text = 'dive into …' ④
>>> print (lxml. etree .tounicode (new_feed )) ⑤
< title type
='html' >dive into & ;hellip ;< /title >< /feed >
>>> print (lxml. etree .tounicode (new_feed , pretty_print =True )) ⑥

dive into& ;hellip ;< /title > <br /> </feed > <br /> ① Для создания подэлемента сущестmxs_]h�we_f_glZ�g_h[oh^bfh�kha^Zlv� <br /> объект класса SubElement. В конструктор класса передаются элемент <br /> родителя (в данном случае new_feed) и имя ноh]h�we_f_glZ��Fu�g_� <br /> объяey_f�aZghо пространстh�bfzg�^ey�kha^Z\Z_fh]h�ihlhfdZ��lZd�dZd�hg� <br /> наследует пространстh�bfzg�hl�jh^bl_ey�. <br /> ② Также мы передаём словарь с атрибутами для элемен та. В качест_�bfzg� <br /> атрибуто�ыступают ключи словаря, в качест_�agZq_gbc�Zljb[mlh - <br /> значения словаря. <br /> ③ Неудиbl_evgh��qlh�ghый элемент title был создан �ijhkljZgklе Atom и <br /> яey_lky�ih^we_f_glhf�we_f_glZ��I�H�H�G��LZd�dZd�we_f_gl��W�L�W�O�H�g_�bf__l� <br /> текстоh]h�kh^_j`Zgby�b�ih^we_f_glh, то lxml сериализует его как пустой <br /> элемент и закрывает симheZfb���!�. <br /> ④ Для то го чтобы добаblv�l_dklh\h_�kh^_j`Zgb_��fu�aZ^Zzf�kойство .text. <br /> ⑤ Теперь элемент title сериализуется с только что заданным текстоuf� <br /> содержанием. Если �l_dkl_�kh^_j`Zlky�agZdb��f_gvr_�q_f����beb<br /> <br /> 196 <br /> <br /> «амперсанд» ', то при сериализации они должны быть экранир оZgu��H�V�F�D�S�e - <br /> последоZl_evghklvx��LZdb_�kblmZpbb��O�[�P�O�h[jZ[Zlu\Z_l�Z\lhfZlbq_kdb�. <br /> ⑥ При сериализации Вы можете применить «приятную печать» («pretty <br /> printing»), при которой klZляется разры�kljhdb�ihke_�aZdjuающего тэга <br /> или открыZxs_]h�lw]Z�we_f_glh с подэлементами но без текстоh]h� <br /> содержания. С технической точки зрения lxml добаey_l�g_agZqZsb_�ijh[_eu� <br /> и переносы строк («insignificant whitespace») чтобы u\_klb��;�0�/�[he__� <br /> читаемым. <br /> <br /> Вам, hafh`gh��[m^_l�bgl_j_kgh�ihijh[hать ещё одну стороннюю <br /> библиот еку xmlwitch , которая повсеместно использует оператор Python <br /> with =для того чтобы сделать код создания XML более читаемым. = <br /> Синтаксический разбор нецелых XML<br /> XML спецификация предписыZ_l��qlh� все XML синтаксические анализаторы <br /> должны uihegylv��« драконоm (строгую) обработку ошибок». То есть, при <br /> обнаружении ��;�0�/�^hdmf_gl_�nhjfZevghc�hrb[db�beb� <br /> не «праbevghihkljh_gghklb�» (wellformedness ) анализаторы должны сразу <br /> же прерZlv�ZgZeba�b��\kiuogmlv�» . Ошибки праbevghihkljh_gghklb� <br /> dexqZxl�g_kh]eZkhанность открыZxsbo�b�aZdjuающих тэгов, <br /> неопределённые элементы, непраbevgu_�kbfолы Юникод и другие <br /> эзотерические ситуации. Такая обработка ошибок сильно контрастирует на <br /> фоне других из_klguo�nhjfZlh\�, например, HTML — браузер не <br /> останаebается отрисовыZlv��Z�H�b -страницу если �kljZgbp_�aZ[ul� <br /> закрыZxsbc��+�7�0�/�lw]�beb�agZq_gb_�Zljb[mlZ�lw]Z�kh^_j`bl� <br /> неэкранироZgguc�Zfi_jkZg^��� Kms_klует распространённое заблуждение, <br /> что в формате HTML не оговорена обра ботка ошибок. На самом деле, <br /> обработка HTML ошибок отлично документироZgZ��gh�hgZ�]hjZa^h�keh`g__�q_f� <br /> просто «останоblvky�b�aZ]hj_lvky�gZ�i_jой ошибк е».) <br /> Некоторые считают (и я в том числе), что это было ошибкой со стороны <br /> разработчиков формата XML заставлять так строго обрабатыZlv�hrb[db��G_� <br /> поймите меня непраbevgh��y�dhg_qgh�`_�aZ�mijhs_gb_�ijZил обработки <br /> ошибок. Однако, на практике понятие «прав ильнопостроенности» оказыZ_lky� <br /> коварнее чем кажется, особенно для XML документов которые публикуются в <br /> интернете и передаются по протоколу HTTP (например, фиды Atom). Несмотря <br /> на зрелость XML, который стандартизоZe�^jZdhghу обработку ошибок � <br /> 1997, иссл едования постоянно показыZxl��qlh�agZqbl_evgZy�qZklv�nb^h\� <br /> Atom в интернете содержат ошибки праbevghihkljh_gghklb�. <br /> Итак, у меня есть и теоретические и практические причины обрабатыZlv��;�0�/� <br /> документы «любой ценой», то есть не останаebаться и взрыZlvky� при <br /> перhc�hrb[d_��?keb�<u�hdZ`_l_kv� похожей ситуации, то lxml может помочь. <br /> Ниже при_^zg�njZ]f_gl��[blh]h���;�0�/�^hdmf_glZ�.<br /> <br /> 197 <br /> <br /> <?xml version ='1.0' encoding ='utf -8' ?> <br /> <feed xmlns ='http://www.w3.org/2005/Atom' xml:lang ='en' > <br /> <title >dive into …
...

В фиде ошибка, так как последоZl_evghklv KHOOLSg_hij_^_e_gZ\nhjfZl_
XML (она определена в HTML). Если попробоZlvjZah[jZlv[blucnb^k
настройками по умолчанию, то lxml споткнётся на неопределённом oh`^_gbb
hellip.
>>> import lxml. etree
>>> tree = lxml. etree .parse ('examples/feed -broken.xml' )
Traceback (most recent call last ):
File "" , line 1, in
File "lxml.etree.pyx" , line 2693 , in lxml. etree .parse (src/lxml/l
xml. etree .c:52591 )
File "parser.pxi" , line 1478 , in lxml. etree ._parseDocument (src/l
xml/lxml. etree .c:75665 )
File "parser.pxi" , line 1507 , in lxml. etree ._parseDocumentFromURL
(src/lxml/lxml. etree .c:75993 )
File "parser.pxi" , line 1407 , in lxml. etree ._parseDocFromFile (sr
c/lxml/lxml. etree .c:75002 )
File "parser.pxi" , line 965 , in lxml. etree ._BaseParser._parseDocF
romFile (src/lxml/lxml. etree .c:72023 )
File "parser.pxi" , line 539 , in lxml. etree ._ParserContext._handle
ParseResultDoc (src/lxml/lxml. etree .c:67830 )
File "parser.pxi" , line 625 , in lxml. etree ._ handleParseResult (sr
c/lxml/lxml. etree .c:68877 )
File "parser.pxi" , line 565 , in lxml. etree ._raiseParseError (src/
lxml/lxml. etree .c:68125 )
lxml. etree .XMLSyntaxError : Entity 'hellip' not defined , line 3, col
umn 28
Для того чтобы обрабатыZlv;0/^hdmf_glk ошибками, необходимо создать
ноuckbglZdkbq_kdbcZgZebaZlhj;0/.

198

>> parser = lxml. etree .XMLParser (recover =True ) ①
>>> tree = lxml. etree .parse ('examples/feed -broken.xml' , parser ) ②
>>> parser .error_log ③
examples/feed -broken. xml :3:28 :FATAL:PARSER:ERR_UNDECLARED_ENTITY: E
ntity 'hellip' not defined
>>> tree. findall ('{http://www.w3.org/2005/Atom}title' )
[]
>>> title = tree. findall ('{http://www.w3.org/2005/Atom}title' )[ 0]
>>> title. text ④
'dive into '
>>> print (lxml. etree .tounicode (tree. getroot ())) ⑤

dive into
.
. [остальной вывод сериализации пропущен для краткости ]
.
① Для того чтобы создать ноucZgZebaZlhjfukha^Zzfghый класс
lxml.etree.XMLParser. Хотя он может принимать много разных параметров , для
нас предстаey_lbgl_j_klhevdhh^bg — аргумент hkklZghления recover.
При присh_gbbZj]mf_glmagZq_gby7UXHO[PO[m^_lbadh`bон лезть чтобы
hkklZghить ошибки праbevghihkljh_gghklb.
② Для т ого чтобы разобрать XML документ ноufZgZebaZlhjhffu
передаём объект parser dZq_klе lhjh]hZj]mf_glZ функцию parse(). На
этот раз lxml не выбрасыZ_lbkdexqbl_evgmxkblmZpbxijbg_hij_^_ezgghc
последоZl_evghklb KHOOLS.
③ Анализатор содержит сооб щения обо всех найденных ошибках. (На самом
деле эти сообщения сохраняются незаbkbfhhliZjZf_ljZUHFRYHU)
④ Так как анализатор не знает что делать с неопределённым …, то он
просто u[jZkuает слово. Текстовое содержание элемента title
преjZsZ_lk я в 'dive into '.
⑤ И ещё раз: после сериализации последоZl_evghklv KHOOLSbkq_aeZO[PO
её u[jhkbe.
Важно отметить, что нет никакой гарантии переносимости hkklZghления
ошибок у XML анализаторов. Другой анализатор может быть умнее и
распознать что & hellip; яey_lkyалидной последоZl_evghklvx+70/b

199

hkklZghить её как амперсанд. «Лучше» ли это? Возможно. Яey_lkyebwlh
«более праbevguf"G_llZddZdh[Zj_r_gbyklhqdbaj_gbynhjfZlZ;0/
не_jguIjZ\bevgh_ihедение (согласно XML спецификации) прекратить
обработку и загореться. Если же необходимо не следоZlvki_pbnbdZpbblh
Вы делаете это на сhckljZobjbkd.
Материалы для дальнейшего чтения
XML на Википедии
The ElementTree XML API (англ .)
Elements and Element Trees - Элементы и дереvy элементов (англ .)
XPath Support in ElementTree - Поддержка XPath в ElementTree (англ .)
The ElementTree iterparse Function - Функция iterparse в ElementTree (англ .)
lxml (англ .)
Parsing XML and HTML with lxml - обработка XML и HTML в lxml (англ .)
XPath and XSLT with lxml - XPath и XSLT в lxml (англ .)
xmlwitch (ан гл .)

200

Сериализация
объекто
PWKRn

Погружение
С перh]hзгляда, идея сериализации проста. У Zk_klvkljmdlmjZ^Zgguo
памяти, которую вы хотите сохранить, использоZlvihторно, или отпраblv
кому либо. Как Zfwlhk^_eZlv"WlhaZисит от того как u__khojZgbl_dZd
u__ohlbl_bkihevahать, и кому u__ohlbl_hlijZить. Многие игры
позheyxlам сохранять Zrijh]j_kki_j_^ыходом и hah[ghлять игру
после запуска. (Вообще, многие неигроu_ijbeh`_gbylZd`_ihaоляют это
делать). В этом случае, структура, которая хранит Zrijh]j_kk игре, должна
быть сохранена на диске, когда вы закрыZ_l_b]jmbaZ]jm`_gZk^bkdZdh]^Z
u__aZimkdZ_l_>Zggu_ij_^gZagZq_gulhevdh^eybk пользоZgbylhc`_
программой что и создала их, никогда не посылаются по сети, и никогда не
читаются ничем кроме программы их создавшей. Поэтому проблемы
соf_klbfhklbh]jZgbq_gul_fqlh[u[he__iha^gb_ерсии программы
могли читать данные созданные ранни ми _jkbyfb.
Для таких случаеfh^mevSLFNOHb^_Ze_gWlhqZklvklZg^Zjlghc[b[ebhl_db
Python, поэтому он всегда доступен. Он быстрый, большая часть написана на
C, как и сам интерпретатор Python. Он может сохранять со_jr_ggh
произhevgu_dhfie_dkgu_kljmd туры данных Python.
Что может сохранять модуль pickle?
 Все kljh_ggu_lbiu^Zgguo3\WKRQlbiERROHDQ,QWHJHUqbkeZk
плавающей точкой, комплексные числа, строки, объекты bytes, массиu
байт, и None.

201

 Списки, кортежи, словари и множестZkh^_j`Zsb_ex[mñ комбинацию
kljh_gguolbih данных
 Списки, кортежи, словари и множестZkh^_j`Zsb_ex[mxdhf[bgZpbx
списков, кортежей, словарей и множеств содержащий любую
комбинацию kljh_gguolbih\^Zgguo blZd^Ze__\iehlv^h
максимального уроgyложенности, котор ый поддержиZ_l3\WKRQ .
 Функции, классы и экземпляры классо kFDYHDWV .
Если для вас этого мало, то модуль pickle еще и расширяем. Если Zf
интересна эта hafh`ghklvlhkfhljbl_kkuedb\jZa^_e_>Zevg_cr__
чтение» в конце этой глаu.
Маленькая заметка о примерах в этой главе.
Эта часть по_kl\m_lh^ух Python консолях. Все примеры wlhc]eZе —
часть одной большей истории. Вам нужно будет переключаться назад и
i_j_^f_`^m^\mfydhgkheyfb^ey^_fhgkljZpbbfh^me_cSLFNOHbMVRQ.
Для того чтобы не запу таться откройте консоль Python и определите
следующую переменную:
>>> shell = 1
Остаvl_wlhhdghhldjulufBhldjhcl__s_h^gmdhgkhev3\WKRQb
определите следующую переменную:
>>> shell = 2
В этой гла_y[m^mbkihevahать переменную shell для того чтобы показать
какую именно консоль Python я использую dZ`^hfijbf_j_.
Сохранение данных в файл Pickle.
Модуль Pickle работает со структурами данных. Давайте создадим одну .
>>> shell 1 ①
>>> entry = {} ②
>>> entry ['title' ] = 'Dive into history, 2009 edition'
>>> entry ['article_link' ] = 'http://diveintomark.org/archives/2009/03/27/dive -into -
history -2009 -edition'
>>> entry ['comments_link' ] = None
>>> entry ['internal_id' ] = b'\xDE \xD5 \xB4 \xF8'
>>> entry ['tags' ] = ('diveintopython' , 'docbook' , 'html' )
>>> entry ['published' ] = True
>>> import time
>>> entry ['published_date' ] = time .strptime ('Fri Mar 27 22:20:42 2009' ) ③
>>> entry ['published_date' ] time .struct_time (tm_year =2009 , tm_mon =3,

202

tm_mday =27 , tm_hour =22 , tm_min =20 , tm_sec =42 , tm_wday =4, tm_yday =86 ,
tm_isdst =-1)
① Все дальнейшее происходит dhgkheb3\WKRQ.
② Идея в том чтобы создать словарь, который будет предстаeylvqlh -нибудь
полезное, например элемент рассылки Atom. Также я хочу быть у_j_gguf
что он содержит несколько разных типо^Zgguoqlh[ujZkdjulvозможности
модуля pickle. Не qbluайтесь слишком сильно wlbi_j_f_ggu_.
③ Модуль time содержит структуру данных (struct_time) для предстаe_gby
момента j_f_gb плоть до миллисекунд) и функции для работы с этими
структурами. Функция strptime() принимает на oh^nhjfZlbjhанную строку и
преобразует ее VWUXFWBWLPHWlZkljhdZ стандартном формате, но u
можете контролироZlv__ijbihfhsbdh^h форматироZgby>ey[he__
подробного описания загляните в модуль time.
Теперь у нас есть замечательный слоZjv>Z\Zcl_khojZgbf_]h файл.
>>> shell ①
1
>>> import pickle
>>> with open ('entry.pickle' , 'wb' ) as f: ②
... pickle .dump (entry , f) ③
...
① Мы все еще в перhcdhgkheb
② Используйте функцию open() для того чтобы открыть файл. Устаноbf
режим работы с файлом 
ZE
^eylh]hqlh[uhldjulvnZce^eyaZibkb
дhbqghfj_`bf_H[_jg_f_]h конструкцию with для того чтобы быть
у_j_gguf том что файл закроется аlhfZlbq_kdbd огда uaZершите
работу с ним.
③ Функция dump() модуля pickle принимает сериализуемую структуру данных
Python, сериализует ее в дhbqguc3\WKRn -заbkbfucnhjfZlbkihevam_l
последнюю _jkbxijhlhdheZSLFNOHbkhojZgy_l__\hldjulucnZce.
Последнее предло жение было очень Z`guf.
 Протокол pickle заbkblhl3\WKRQa^_kvg_l]ZjZglbckhместимости с
другими языками. Вы hafh`ghg_kfh`_l_\aylvHQWU\SLFNOHnZce
который только что сделали и как — либо с пользой его использоZlvijb
помощи Perl, PHP, Java ил и любого другого языка программироZgby
 Не kydZykljmdlmjZ^Zgguo3\WKRQfh`_l[ulvk_jbZebahана модулем
Pickle. Протокол pickle менялся несколько раз с добавлением ноuo
типо^Zgguo язык Python, и все еще у него есть ограничения.
 Как результат, нет г арантии соf_klbfhklbf_`^mjZagufbерсиями
Python. Ноu_ерсии Python поддержиZxlklZju_nhjfZlu

203

сериализации, но старые _jkbb3\WKRQg_ih^^_j`b\Zxlgh\u_
форматы (поскольку не поддержиZxlghые форматы данных)
 Пока ug_mdZ`_l_bgh_nmgdpbbfh^me я pickle будут использоZlv
последнюю _jkbxijhlhdheZSLFNOHWlhk^_eZgh^eymеренности lhf
что вы имеете наибольшую гибкость lbiZo^Zgguodhlhju_ы можете
сериализоZlvghwlhlZd`_agZqblqlhj_amevlbjmxsbcnZce[m^_l
неhafh`ghijhqblZlvijb помощи старых _jkbc3\WKRQdhlhju_g_
поддержиZxlihke_^gxxерсию протокола pickle.
 Последняя _jkbyijhlhdheZSLFNOHwlh^оичный формат. Убедитесь, что
открыZ_l_nZceuSLFNOH\^оичном режиме, или данные будут
поj_`^_guijbaZibkb.
Загрузка данны х из фала pickle.
Теперь переключитесь hторую консоль Python — т. е. не lm]^_ы создали
словарь entry.
>>> shell ①
2
>>> entry ②
Traceback (most recent call last ):
File " ", line 1, in
NameError : name 'entry' is not defined
>>> import pickle
>>> with open ('entry.pickle' , 'rb' ) as f: ③
... entry = pickle .load (f) ④
...
>>> entry ⑤
{'comments_link' : None ,
'internal_id' : b '\xDE \xD5 \xB4 \xF8' ,
'title' : 'Dive into history, 2009 edition' ,
'tags' : ('diveintopython' , 'docbook' , 'html' ),
'article_link' :
'http://diveintomark.org/archives/2009/03/27/dive -into -history -2009 -edition' ,
'published_date' : time .struct_time (tm_year =2009 , tm_mon =3, tm_mday =27 ,
tm_hour =22 , tm_min =20 , tm_sec =42 , tm_wday =4, tm_yday =86 , tm_isdst =-1),
'published' : True }
① Это lhjZydhgkhev3\WKRn
② Здесь не определена переменная entry. Вы определяли переменную entry 
перhcdhgkheb3\WKRQghwlhiheghklvxhlebqgh_hdjm`_gb_khkоим
собст_ggufkhklhygb_f.

204

③ Откроем entry.pickle файл, который ukha^Zeb\i_jой консоли Python.
Модуль pickle испол ьзует дhbqgucnhjfZlij_^klZ\e_gby^Zgguoihwlhfm
Zfсегда нужно открыZlvnZce дhbqghfj_`bf_.
④ Функция pickle.load() принимает на oh^ihlhdqblZ_lk_jbZebah\Zggu_
данные из потока, создает ноuch[t_dl3\WKRQосстанаebает
сериализоZggu_^w нные в ноuch[t_dl3\WKRQbозjZsZ_lghый объект
Python.
⑤ Теперь переменная entry — это словарь со знакомыми ключами и
значениями.
Результат цикла pickle.dump()/pickle.load() это ноZykljmdlmjZ^Zgguo
экbалентная оригинальной структуре данных.
>>> shell ①
1
>>> with open ('entry.pickle' , 'rb' ) as f: ②
... entry2 = pickle .load (f) ③
...
>>> entry2 == entry ④
True
>>> entry2 is entry ⑤
False
>>> entry2 ['tags' ] ⑥
('diveintopython' , 'docbook' , 'html' )
>>> entry2 ['internal_id' ]
b'\xDE \xD5 \xB4 \xF8'
① Переключитесь обратно в перmxdhgkhev3\WKRQ.
② Откройте entry.pickle файл
③ Загрузите сериализоZggu_^Zggu_ ноmxi_j_f_ggmxHQWU\2
④ Python подт_j`^Z_lqlhwlb^а словаря(entry и entry2) экbалентны. В
этой консоли ukha^ZebHQWU\kgmeygZqbgZykimklh]hkehаря вручную
присZbая значения ключам. Вы сериализоZebwlhl словарь и сохранили 
файле entry.pickle. Теперь ukqblZebk_jbZebahанные данные из этого фала
и создали со_jr_ggmxdhibxhjb]bgZevghckljmdlmju.
⑤ Экbалентность не значит идентичности. Я сказал, что ukha^Zeb
_идеальную копию_ оригинальной структуры данных, и это пра^ZGhwlhсе
же копия.
⑥ По причинам которые станут ясны ^Zevg_cr_fyohqmmdZaZlvqlh
значения ключа 'tags' это кортеж, и зн ачение 'internal_id' это объект bytes.

205


Много статей о модуле Pickle ссылаются на cPickle. В Python 2 сущестm_l
д_j_ZebaZpbbfh^meySLFNOHh^gZgZibkZgZgZqbklhf3\WKRQZ^jm]ZygZ
C(но все же uau\Z_fZba3\WKRQ <3\WKRQwlb^а модуля были
объеденены поэтому Zfke_^m_l\k_]^Zbkihevahать import pickle. Вам
могут быть плезны эти статьи но следует игнорироZlvmklZj_шую
информацию о cPickle.
Используем Pickle без файлов
Пример из предыдущей секции показал как сериализовать объект напрямую 
файл на диске. Но что если он вам не нужен или ug_ohl_ebbkihevah\Zlv
файл? Вы можете сериализоZlv\h[t_dlE\WHV\iZfylb.
>>> shell
1
>>> b = pickle .dumps (entry ) ①
>>> type (b) ②

>>> entry3 = pickle .loads (b) ③
>>> entry3 == entry ④
True
① Функция pickle.dumps() (обратите gbfZgb_gZ
V
\dhgp_bf_gbnmgdpbb
делает ту же самую сериализацию что и функция pickle.dump(). Вместо того
чтобы принимать на oh^ihlhdbibkZlvk_jbZebahанные данные на диск,
она просто haращает сериализоZggu_{ анные
② Поскольку протокол pickle использует дhbqgucnhjfZl^Zgguonmgdpby
pickle.dumps() haращает объект типа bytes.
③ Функция pickle.loads() (сноZaZf_lvl_
V
 конце имени функции) делает ту
же самую десериализацию что и функция pickle.load(). Но f_klhlh]hqlh[u
принимать на oh^ihlhdbqblZlvk_jbZebahанные данные из файла, она
принимает на oh^h[t_dllbiZE\WHVkh^_j`Zsbck_jbZebahанные данные,
такие как ha\jZsZ_fu_nmgdpb_cSLFNOHGXPSV )
④ Конечный результат таков же: идеальная копия ори гинального словаря.
Байты и строки снова вздымают свои уродливые головы
Протокол pickle сущестm_lm`_fgh]he_lbhgjZaивался f_kl_kl_fdZd
разbался сам Python. Сейчас сущестm_lq_luj_jZaebqguoерсии
протокола pickle.
 Python 1.x породил д_ _jkbbijhlhdheZhkghанный на тексте формат
(_jkby b^оичный формат (_jkby)

206

 Python 2.3 ел ноucijhlhdheSLFNOH ерсия 2) для того чтобы
поддержиZlvgh\ucnmgdpbhgZe\deZkkZo3\WKRQHg^оичный.
 Python 3.0 ел еще один протокол pickle(_jkb я 3) с полной поддержкой
объектов типа bytes и массиh\[ZclHglZd`_^оичный.
Вау смотрите, разница между строками и байтами сноZздымает сhx
уродливую голоm ?kebы удиe_guы не уделяли достаточно gbfZgby
На практике это значит, что в то в ремя, как Python 3 может читать данные
сохраненные при помощи протокола _jkbb3\WKRQg_fh`_lqblZlv
данные сохраненные при помощи протокола версии 3.
Отладка файлов pickle
Как u]ey^blijhlhdheSLFNOH">Z\Zcl_g_gZ^he]hhleh`bfdhgkhevS\WKRQb
a]e янем nZceHQWU\SLFNOHdhlhjucfukha^Zeb>eyg_ооруженного
a]ey^Zhgыглядит как тарабарщина.
you @ localhost:~ /diveintopython3 /examples$ ls -l entry.pickle
-rw -r--r-- 1 you you 358 Aug 3 13 :34 entry.pickle
you @ localhost:~ /diveintopython3 /examples$ cat entry.pickle
comments_linkqNXtagsqXdiveintopythonqXdocbookqXhtmlq?qX publishedq?
XlinkXJhttp: //diveintomark.org /archives /2009 /03 /27 /dive -into -history -2009 -edition
q Xpublished_dateq
ctime
struct_time
?qRqXtitleqXDive into history , 2009 editionqu.
Не слишком то полезно. Вы можете b^_lvkljhdbghhklZevgu_lbiu^Zgguo
u]ey^yldZdg_i_qZlZ_fu_ bebdZdfbgbfmfg_qblZ_fu_ kbfолы. Поля
даже не разделены хотя бы табуляцией или пробелами. Это не тот формат,
который вы бы захотели отлажив ать jmqgmx.
>>> shell
1
>>> import pickletools
>>> with open ('entry.pickle' , 'rb' ) as f:
... pickletools .dis (f)
0: \x80 PROTO 3
2: } EMPTY_DICT
3: q BINPUT 0
5: ( MARK
6: X BINUNICODE 'published_date'
25 : q BINPUT 1
27 : c GLOBAL 'time struct_time'
45 : q BINPUT 2

207

47 : ( MARK
48 : M BININT2 2009
51 : K BININT1 3
53 : K BININT1 27
55 : K BININT1 22
57 : K BININT1 20
59 : K BININT1 42
61 : K BININT1 4
63 : K BININT1 86
65 : J BININT -1
70 : t TUPLE (MARK at 47 )
71 : q BINPUT 3
73 : } EMPTY_DICT
74 : q BINPUT 4
76 : \x86 TUPLE2
77 : q BINPUT 5
79 : R REDUCE
80 : q BINPUT 6
82 : X BINUNICODE 'comments_link'
100 : q BINPUT 7
102 : N NONE
103 : X BINUNICODE 'internal_id'
119 : q BINPUT 8
121 : C SHORT_BINBYTES 'ÞÕ´ø'
127 : q BINPUT 9
129 : X BINUNICODE 'tags'
138 : q BINPUT 10
140 : X BINUNICODE 'diveintopython'
159 : q BINPUT 11
161 : X BINUNICODE 'docbook'
173 : q BINPUT 12
175 : X BINUNICODE 'html'
184 : q BINPUT 13
186 : \x87 TUPLE3
187 : q BINPUT 14
189 : X BINUNICODE 'title'
199 : q BINPUT 15
201 : X BINUNICODE 'Dive into history, 2009 edition'
237 : q BINPUT 16
239 : X BINUNICODE 'article_link'
256 : q BINPUT 17
258 : X BINUNICODE 'http://diveintomark.org/archives/2009/03/27/div e-into -

208

history -2009 -edition'
337 : q BINPUT 18
339 : X BINUNICODE 'published'
353 : q BINPUT 19
355 : \x88 NEWTRUE
356 : u SETITEMS (MARK at 5)
357 : . STOP
highest protocol among opcodes = 3
Самая интересная часть информации ^baZkk_f[e_j_gZoh^blkygZ
последней строке, потому что она dexqZ_lерсию протокола, при помощи
которого данный файл был сохранен. Не сущестm_lyного маркера
протокола pickle. Чтобы определить какую _jkbxijhlhdheZbkihev зовали
для сохранения фала Pickle, Zfg_h[oh^bfhaZ]eygmlv\fZjd_ju RSFRGHV
gmljbkhojZg_gguo^Zgguobbkihevahать вшитую информацию о том какие
маркеры были едены, в какой _jkbbijhlhdheZ3LFNOHNmgdpby
pickletools.dis() делает именно это, и он а печатает результат в последней
строке дизассемблироZggh]h\uода. Вот функция, которая возjZsZ_l
только номер _jkbb[_aывода данных:
import pickletools

def protocol_version (file_object ):
maxproto = -1
for opcode , arg , pos in pickletools .genops (file_object ):
maxproto = max (maxproto , opcode. proto )
return maxproto

И hl она же в дейстbb :
>>> import pickleversion
>>> with open ('entry.pickle' , 'rb' ) as f:
... v = pickleversion. protocol_version (f)
>>> v
3
Сериализация объектов Python для чтения при помощи других
языков
Формат данных используемый модулем pickle Python -заbkbfucHgg_
пытается быть совместимым с другими языками программироZgby?keb
межязыкоZykh\f_klbfhklv_klvkj_^bаших потребностей, Zfke_^m_l
присмотрет ься к форматам сериализации. Один из таких формато-621
«JSON» это аббреbZlmjZhl-DYD6FULSW2EMHFW1RWDWLRQghg_ihaоляйте

209

имени обмануть вас — JSON был на_jgydZjZajZ[hlZg^eybkihevah\Zgby
многими языками программироZgby.
Python 3 dexqZ_lfh{ уль json в стандартную библиотеку. Как и модуль pickle,
модуль json имеет функции для сериализации структур данных, сохранения
сериализоZgguo^ZgguogZ^bkdaZ]jmadbk_jbZebahанных данных с диска, и
десереализации данных обратно в ноuch[t_dl3\WKRQi ак же существует
несколько Z`guojZaebqbcI_j\h_nhjfZl^ZgguoMVRQl_dklh\ucZg_
дhbqguc RFC 4627 определяет формат json и то, как различные типы данных
должны быть преобразоZgu\l_dklGZijbf_jeh]bq_kdh_agZq_gb_
сохраняется как пяти симhevgZykljhdZ
IDOVH
bebq_luj_okbfольная строка
'true'. Все значения MVRQj_]bkljhqmстbl_evgu_.
Во — вторых, как и с любым текстовым форматом, сущестm_lijh[e_fZ
пробело-621ihaоляет klZлять произhevgh_dhebq_klо пробело
(табуляций, переводов строк, и пустых строк) между значениями. Пробелы 
нем «незначащие», что значит, кодироsbdb-621fh]ml{ обаeylvlZdfgh]h
или так мало пробелов как захотят, и декодироsbdb-621[m^ml
игнорироZlvijh[_euf_`^magZq_gbyfbWlhihaоляет Zfbkihevah\Zlv
красивый u\h^ SUHWWy -print) для отображения Zrbo^Zgguo формате JSON,
удобно отображать eh`_ggu_agw чения различными уроgyfbhlklmiZlZd
чтобы ufh]ebqblZlv\k_\klZg^Zjlghfijhkfhljsbd_bebl_dklhом
редакторе. Модуль json в Python имеет опции красиh]hывода hремя
кодироZgby^Zgguo.
В — третьих, сущестm_lfgh]he_lgyyijh[e_fZdh^bjhок. JSON хранит
значения как обычный текст, но, как uagZ_l_g_kms_klует таких _s_cdZd
«обычный текст». JSON должен быть сохранен в кодироd_8QLFRGH 87F -32,
UTF -16, или стандартной UTF -8), и секция 3 из RFC 4627 определяет то, как
указать используемую кодироdm.
Сохранение данных в файл JSON
JSON выглядит удиbl_evghihoh`bfgZkljmdlmjm^Zgguodhlhjmxы могли
бы определить в ручную -DYD6FULSWWlhg_kemqZcghы дейстbl_evgh
можете использовать функцию eval() из JavaScript чтобы «декодироZlv
данные сериализоZggu_\MVRQ H[uqgu_ijhl_kluijhlb не до_j_ggh]h
ода принимаются, но дело lhfqlhMVRQwlhdhjj_dlguc-DYD6FULSW Ih
сущестm , JSON может быть уже хорошо знаком Zf .
>>> shell
1
>>> basic_entry = {} ①
>>> basic_entry ['id' ] = 256
>>> basic_entry ['title' ] = 'Dive into history, 2009 edition'
>>> basic_entry ['tags' ] = ('diveintopython' , 'docbook' , 'html' )
>>> basic_entry ['published' ] = True

210

>>> basic_entry ['comments_link' ] = None
>>> import json
>>> with open ('basic.json' , mode ='w' , encoding ='utf -8' ) as f: ②
... json .dump (basic_entry , f) ③
① Мы собираемся создать ноmxkljmdlmjm^Zgguo\f_klhlh]hqlh[u
использоZlvm`_bf_xsmxkykljmdlmjm^ZgguoHQWU\Iha`_\wlhc]eZ\_fu
уb^bfqlhkemqblkydh]^Zfuihijh[m_fdh^bjhать более общую структуру
данных -621.
② JSON это текстоucnhjfZlwl о значит, что вы должны открыть файл 
текстоhfj_`bf_bmdZaZlvdh^bjh\dm<ugbdh]^Zg_hrb[_l_kvbkihevamy
UTF -8.
③ Как и модуль pickle, модуль json определяет функцию dump() которая
принимает на oh^kljmdlmjm^Zgguo3\WKRQbihlhd^eyaZibkbNmgdpb я
dump() сериализует структуру данных Python и записыZ_l__\h[t_dlihlhdZ
Раз мы делаем это dhgkljmdpbbZLWKfufh`_f[ulvmеренными, что файл
будет корректно закрыт, когда мы за_jrbfjZ[hlmkgbf.
Ну и как u]ey^blj_amevlZlk_jbZebaZpbb формат json?
you @ localhost:~ /diveintopython3 /examples$ cat basic.json
{"published" : true , "tags" : ["diveintopython" , "docbook" , "html" ], "comments_link" :
null,
"id" : 256 , "title" : "Dive into history, 2009 edition" }
Это несомненно, намного более читаемо, чем файл pickle. Но json может
содержать произвольное количество пробелов между значениями, и модуль
json предостаey_lijhklhcimlv^eykha^Zgby_s_[he__qblZ_fh]hnZceZ
json.
>>> shell
1
>>> with open ('basic -pretty.json' , mode ='w' , encoding ='utf -8' ) as f:
... json .dump (basic_entry , f, indent =2) ①
① Если u передадите параметр ident функции Json .dump () она сделает
результирующий файл json более читаемым в ущерб размеру файла .
Параметр ident это целое число. 0 значит «расположить каждое значение на
отдельной строке». Число больше 0 значит «расположить каждое значение на
отдельной строке, и использоZlvQXPEHUijh[_eh\^eyhlklmih h
eh`_gguokljmdlmjZo^Zgguo.
И hl результат :
you @ localhost:~ /diveintopython3 /examples$ cat basic -pretty.json
{
"published" : true ,

211

"tags" : [
"diveintopython" ,
"docbook" ,
"html"
],
"comments_link" : null,
"id" : 256 ,
"title" : "Dive into history, 2009 edition"
}
Соответствие типов данных Python к JSON
Поскольку JSON разрабатыZekyg_lhevdh^ey3\WKRQ_klvg_dhlhju_
недочеты в покрытии типов данных Python. Некоторые из них просто различие
gZaании типов, но есть дZажных типа данных Python, которые полностью
упущены из b^mIhkfhljbfkfh`_l_ebы заметить их:
Пометки JSON PYTHON 3

object dictionary

array list

string string

integer integer

real number float
* true True
* false False
* null None
* Текст ячейки =Текст ячейки =
 - Все переменные  JavaScript регистрозаbkbfu|
Вы заметили что потеряно? Кортежи и байты! В JSON есть тип - масси
который модуль JSON стаbl\khhlетстb_lbimkibkhd\3\WKRQghlZfg_l
отдельного типа для "статичных массиh» (кортежей). И также в JSON есть
хорошая по ддержка строк, но нет поддержки объектов типа bytes или массиh\
байт.

212

Сериализация типов данных не поддерживаемых JSON
То что -621g_lстроенной поддержки типа bytes, не значит что ug_
сможете сериализоZlvh[t_dlulbiZE\WHVFh^mevMVRQij_^hklZля ет
расширяемые хуки для кодироZgbyb^_dh^bjhания неизвестных типо
данных. (Под "неиз_klgufbybf_e b^mg_hij_^_e_ggu_\MVRQ
Очеb^ghqlhfh^mevMVRQagZ_lhfZkkbах байт, но он создан с учетом
ограничений спецификации json). Если uohlbl_ закодироZlvlbiE\WHVbeb
другие типы данных, которые json не поддержиZ_l\Zfg_h[oh^bfh
предостаblvhkh[u_dh^bjhщики и декодироsbdb^eywlbolbih данных.
>>> shell
1
>>> entry ①
{'comments_link' : None ,
'internal_id' : b '\xDE \xD5 \xB4 \xF8' ,
'title' : 'Dive into history, 2009 edition' ,
'tags' : ('diveintopython' , 'docbook' , 'html' ),
'article_link' : 'http://diveintomark.org/archives/2009/03/27/dive -into -history -2009 -
edition' ,
'published_date' : time .struct_time (tm_year =2009 , tm_mon =3, tm_mday =27 ,
tm_hour =22 , tm_min =20 , tm_sec =42 , tm_wday =4, tm_yday =86 , tm_isdst =-1),
'published' : True }
>>> import json
>>> with open ('entry.json' , 'w' , encoding ='utf -8' ) as f: ②
... json .dump (entry , f) ③
...
Traceback (most recent call last ):
File "" , line 5, in
File "C: \Python31 \lib \json \__init__.py" , line 178 , in dump
for chunk in iterable:
File "C: \Python31 \lib \json \encoder.py" , line 408 , in _iterencode
for chunk in _iterencode_dict (o, _current_indent_level ):
File "C: \Python31 \lib \json \encoder.py" , line 382 , in _iterencode_dict
for chunk in chunks:
File "C: \Python31 \lib \json \encoder.py" , line 416 , in _iterencode
o = _default (o)
File "C: \Python31 \lib \json \encoder.py" , line 170 , in default
raise TypeError (repr (o) + " is not JSON serializable" )
TypeError : b '\xDE \xD5 \xB4 \xF8' is not JSON serializable
① Хорошо, настало j_fy\ghь обратиться к структуре данных entry. Там
есть все: логические значения, пустое значение, строка, кортеж строк, объект
типа bytes, и структура хранящая j_fy.

213

② Я знаю, что говорил это ранее, но поlhjxkv_s_jZaMVRQwlhl_dkl оuc
формат. Всегда открыZcl_nZceuMVRQ\l_dklhом режиме с кодироdhcXWf -8.
③ Чтож... _ЭТО_ не хорошо. Что произошло?
А hlqlhnmgdpbyMVRQGXPS ihijh[hала сериализоZlvh[t_dlE\WHV
b' \xDE \xD5 \xB4 \xF8', но ей не удалось, потому что в json нет под держки
объектов bytes. Однако, если сохранение таких объектоажно для Zkы
можете определить свой "мини формат сериализации".
def to_json (python_object ): ①
if isinstance (python_object , bytes ): ②
return {'__class__' : 'bytes' ,
'__value__' : list (python_object )} ③
raise TypeError (repr (python_object ) + ' is not JSON serializable' ) ④
① Чтобы определить сhckh[klенный "мини формат сериализации" для тех
типо^ZgguoqlhMVRQg_ih^^_j`b\Z_lbadhjh[dbijhklhhij_^_ebl_
функцию, которая принимает объект Python как параметр. Этот объект Python
будет именно тем объектом, который функция json.dump() не сможет
сериализоZlvkZfZ - в данном случае это объект bytes b' \xDE \xD5 \xB4 \xF8'
② Вашей специфичной функции сериализации следует про_jylvlbi
объектов Python, которые передала ей функция json.dump(). Это не
обязательно, если ZrZnmgdpbyk ериализует только один тип данных, но это
делает кристально ясным какой случай покрыZ_l^ZggZynmgdpbyb^_eZ_l
более простым улучшение функции, если ZfihgZ^h[blkyk_jbZebahать
больше типо^Zgguoiha`|
③ В данном случае я решил кон_jlbjhать объект bytes keh\ZjvDexq
__class__ будет содержать назZgb_hjb]bgZevgh]hlbiZ^ZgguoZdexq
__value__ будет хранить само значение. Конечно, это не может быть объекты
типа bytes, поэтому нужно преобразоZlv_]hо что -нибудь сериализуемое
при помощи json. Об ъекты типа bytes это просто последовательность чисел,
каждое число будет где -то от 0 до 255. Мы можем использоZlvnmgdpbxOLVW
чтобы преобразовать объект bytes в список чисел. Итак b' \xDE \xD5 \xB4 \xF8'
станоblky>@ IhkqblZcl_Wlhjw ботает! Байт \xDE 
шестнадцатеричной системе это 222 ^_kylbqghc \xD5 это 213, и так далее.)
④ Это строка Z`gZKljmdlmjZ^Zgguodhlhjmxы сериализуете может
содержать типы данных которых нет в json, и которые не обрабатыZ_lаша
функция. В таком сл учае, Zrh[jZ[hlqbd^he`_gUDLVHhrb[dm7\SH(UURU
чтобы функция json.dump() узнала, что Zrh[jZ[hlqbdg_kfh]jZkihagZlvlbi
данных.
Вот оно, больше вам ничего не нужно. Дейстbl_evghhij_^_e_ggZyами
функция обработчик haращает слоZjv3\WKRQg_kl року. Вы не пишите
сериализацию в json полностью сами, uijhklh^_eZ_l_dhgертацию -в-
поддержиZ_fuc -тип -данных. Функция json.dump() сделает остальное за Zk.

214

>>> shell
1
>>> import customserializer ①
>>> with open ('entry.json' , 'w' , encoding ='utf -8' ) as f: ②
... json .dump (entry , f, default =customserializer. to_json ) ③
...
Traceback (most recent call last ):
File "" , line 9, in
json. dump (entry , f, default =customserializer. to_json )
File "C: \Python31 \lib \json \__init__.py" , line 178 , in dump
for chunk in iterable:
File "C: \Python31 \lib \json \encoder.py" , line 408 , in _iterencode
for chunk in _iterencode_dict (o, _current_indent_level ):
File "C: \Python31 \lib \json \encoder.py" , line 382 , in _iterencode_dict
for chunk in chunks:
File "C: \Python31 \lib \json \encoder.py" , line 416 , in _iterencode
o = _default (o)
File "/Users/pilgrim/diveintopython3/examples/customserializer.py" , line 12 , in
to_json
raise TypeError (repr (python_object ) + ' is not JSON serializable' ) ④
TypeError : time .struct_time (tm_year =2009 , tm_mon =3, tm_mday =27 , tm_hour =22 ,
tm_min =20 , tm_sec =42 , tm_wday =4, tm_yday =86 , tm_isdst =-1) is not JSON
serializable
① Модуль customserializer, это то где ulhevdhqlhhij_^_ebebnmgdpbx
to_json() ij_^u^ms_fijbf_j|
② Текстоucj_`bfXWf -8, тра -ля -ля. (Вы забудете! Я иногда забыZx_ k_
работает замечательно, пока h^bgfhf_glg_kehfZ_lkyblh]^Zhgh
начинает ломаться еще театральнее)
③ Это Z`gucdmkhdqlh[uстроить Zrmnmgdpbxh[jZ[hlqbd
преобразоZgby\nmgdpbxMVRQGXPS i_j_^Zcl_ашу функцию MVRQGXPS
iZjZf_lj_GHf ault. (Ура, все 3\WKRQ - объект!)
④ Замечательно, это и пра^ZjZ[hlZ_lGhihkfhljbl_gZbkdexq_gb_
Теперь функция json.dump() больше не жалуется о том, что не может
сериализоZlvh[t_dlE\WHVL_i_jvhgZ`Zem_lkyhkhершенно другом
объекте: time.stru ct_time.
Хоть получить другое исключение и не u]ey^bldZdijh]j_kkgZkZfhf^_e_
это так. Нужно просто добаblviZjmkljhddh^Zqlh[ubwlhjZ[hlZeh.
import time

def to_json (python_object ):

215

if isinstance (python_object , time .struct_time ): ①
return {'__class__' : 'time.asctime' ,
'__value__' : time .asctime (python_object )} ②
if isinstance (python_object , bytes ):
return {'__class__' : 'bytes' ,
'__value__' : list (python_object )}
raise TypeError (repr (python_object ) + ' is not JSON serializable' )
① Добаeyy\m`_kms_klующую функцию customserializer.to_json() мы
должны про_jblvqlhh[t_dl3\WKRQ kdhlhjufmnmgdpbbMVRQGXPS
проблемы) на самом деле time.struct_time.
② Если так, мы сделаем нечто похожее на кон_jlZpbxqlhfu^_eZebk
объектом bytes: преобразуем объект time.struct_time в словарь который
содержит только те типы данных что можно сериализоZlv\MVRQ<^Zgghf
случае, простейший путь преобразоZlv^Zlm з начение которое можно
сериализоZlv json это преобразовать ее к строке при помощи функции
time.asctime(). Функция time.asctime() преобразует отjZlbl_evghыглядящую
time.struct_time в строку 'Fri Mar 27 22:20:42 2009'.
С этими дmfyhkh[ufbij_h[jZahан иями, структура данных entry должна
сериализоZlviheghklv[_adZdboeb[hijh[e_f.
>>> shell
1
>>> with open ('entry.json' , 'w' , encoding ='utf -8' ) as f:
... json .dump (entry , f, default =customserializer. to_json )
...
you @ localhost:~ /diveintopython3 /examples$ ls -l example.json
-rw -r--r-- 1 you you 391 Aug 3 13 :34 entry.json
you @ localhost:~ /diveintopython3 /examples$ cat example.json
{"published_date" : {"__class__" : "time.asctime" , "__value__" : "Fri Mar 27 22:20:42
2009" },
"comments_link" : null, "internal_id" : {"__class__" : "bytes" , "__value__" : [222 , 213 ,
180 , 248 ]},
"tags" : ["diveintopython" , "docbook" , "html" ], "title" : "Dive into history, 2009 edition" ,
"article_link" : "http://diveintomark.org/archives/2009/03/27/dive -into -history -2009 -
edition ",
"published" : true }
Загрузка данных из файла json
Как и в модуле pickle в модуле json есть функция load (), которая принимает на
oh^ihlhdqblZ_lbag_]h^Zggu_\nhjfZl_MVRQbkha^Z_lgh\uch[t_dl
Python, который будет копией структуры данных записанной MVRQnZce_.

216

>>> shell
2
>>> del entry ①
>>> entry
Traceback (most recent call last ):
File "" , line 1, in
NameError : name 'entry' is not defined
>>> import json
>>> with open ('entry.json' , 'r', encoding ='utf -8' ) as f:
... entry = json. load (f) ②
...
>>> entry ③
{'comments_link' : None ,
'internal_id' : {'__class__' : 'bytes' , '__value__' : [222 , 213 , 180 , 248 ]},
'title' : 'Dive into history, 2009 edition' ,
'tags' : ['diveintopython' , 'docbook' , 'html' ],
'article_link' : 'http://diveintomark.org/archives/2009/03/27/dive -into -history -2009 -
edition' ,
'published_date' : {'__class__' : 'time.asctime' , '__valu e__' : 'Fri Mar 27 22:20:42
2009' },
'published' : True }
① Для демонстрации переключитесь hторую консоль Python и удалите
структуру данных entry которую ukha^ZebjZg__ этой гла_ijbihfhsb
модуля Pickle.
② В простейшем случае функция json. load () работает так же как и функция
pickle .load (). Вы передаете ей объект потока, а получаете в результате ноuc
объект Python.
③ У меня есть хорошие и плохие ноhklbOhjhrb_ghости в том, что
функция json. load () успешно прочитала файл entry.json, который u создали 
перhcdhgkheb3\WKRQbkha^ZeZgh\uch[t_dl3\WKRQdhlhjuckh^_j`bl
данные. А теперь плохие ноhklbhgZg_оссоздала оригинальную структуру
данных entry. ДZagZq_gby 'internal_id' и 'published_date' были созданы как
словари - а именно, как с ловари со значениями которые соf_klbfukMVRQ
(именно их ukha^Zeb функции преобразоZgby to_json () )
Функция json. load () ничего не знает о функции преобразоZgbydhlhjmxы
могли передать в json. dump (). Теперь Zfgm`ghkha^Zlvnmgdpbxh[jZlgmx
to_j son () — функцию, которая примет u[hjhqghij_h[jZahанный json объект
и преобразует его обратно в оригинальный объект Python.
# add this to customserializer.py
def from_json (json_object ): ①
if '__class__' in json_object: ②

217

if json_object ['__class__' ] == 'time.asctime' :
return time .strptime (json_object ['__value__' ]) ③
if json_object ['__class__' ] == 'bytes' :
return bytes (json_object ['__value__' ]) ④
return json_object
① Эта функция преобразоZgbylZd`_ijbgbfZ_lh^bgiZjZf_ljb
haращает одно значение. Но параметр который она принимает - не строка,
это объект Python - результат десереализации строки JSON, в которую был
преобразоZgh[t_dl3\WKRQ.
② Все что Zfgm`ghlZdwlhijhерить, содержит ли данный объект ключ
'__class__' , который создала функция to_json (). Е сли так, то значение
найденное по этому ключу, расскажет ZfdZd^_dh^bjhать этот объект
обратно в оригинальный объект Python
③ Чтобы декодироZlvkljhdmремени haращаемую функцией
time .asctime () Zfgm`ghbkihevahать функцию time .strptime (). Эта фун кция
принимает параметром форматированную строку j_f_gb  определяемом
формате, но по умолчанию этот формат соiZ^Z_lknhjfZlhf time .asctime ())
и haращает time .struct_time
④ Для преобразоZgbykibkdZqbk_eh[jZlgh объекты bytes ufh`_l_
использоZlv функцию bytes ()
Вот и все, k_]h^а типа данных которые были обработаны функцией to_json ()
и теперь эти же типы данных были обработаны функцией from_json (). Вот
результат :
>>> shell
2
>>> import customserializer
>>> with open ('entry.json' , 'r', encoding ='utf -8' ) as f:
... entry = json. load (f, object_hook =customserializer. from_json ) ①
...
>>> entry ②
{'comments_link' : None ,
'internal_id' : b '\xDE \xD5 \xB4 \xF8' ,
'title' : 'Dive into history, 2009 edition' ,
'tags' : ['diveintopython' , 'docbook' , 'html' ],
'article_link' : 'http://diveintomark.org/archives/2009/03/27/dive -into -history -2009 -
edition' ,
'published_date' : time .struct_time (tm_year =2009 , tm_mon =3, tm_mday =27 ,
tm_hour =22 , tm_min =20 , tm_sec =42 , tm_wday =4, tm_yday =86 , tm_isdst =-1),
'published' : True }

218

① Чтобы встроить функцию from_json () в процесс десериализации, передайте
ее iZjZf_lj_REMHFWBKRRN\ызо_nmgdpbb json. load (). Функции которые
принимают функции, как удобно!
② Структура данных entry теперь содержит ключ 'internal_id' со значением
типа bytes. Также она содержит ключ 'published_date' со значением
time .struct_time .
Хотя остался еще один глюк.
>>> shell
1
>>> import customserializer
>>> with open ('entry.json' , 'r', encoding ='utf -8' ) as f:
... entry2 = json. load (f, object_hook =customserializer. from_json )
...
>>> entry2 == entry ①
False
>>> entry ['tags' ] ②
('diveintopython' , 'docbook' , 'html' )
>>> entry2 ['tags' ] ③
['diveintopython' , 'docbook' , 'html' ]
① Даже после встраиZgbynmgdpbb to_json () в сериализацию и функции
from_json () ^_k_jbZebaZpbxfu\k__s_g_ihemqbebihegmxdhibx
оригинальной структуры данных. Почему нет?
② В оригинальной структуре данных entry значение по ключу 'tags' было
кортежем строк.
③ Но в hkkha^Zgghckljmdlmj_^ZgguoHQWU\agZq_gb_ihdexqm 'tags' - эт о
список строк. JSON не b^bljZaebqbcf_`^mdhjl_`ZfbbkibkdZfb\g_f
есть только один похожий на список тип данных, массиbfh^mevMVRQih -
тихому преобразует и списки и кортежи fZkkbы json h\j_fy
сериализации. Для большинстZkemqZ_, ufh`_l_ проигнорироZlv
различие между списками и кортежами, но это нужно помнить, когда
работаешь с модулем json.
Материалы для дальнейшего чтения
 О модуле pickle:
o PEP 238: Изменение оператора деления
 О формате JSON и модуле json:
o json — JavaScript Object Notation Serializer
o JSON encoding a nd ecoding with custom objects in Python

219

HTTP и веб -
серbku

Погружение
HTTP _x -серbkuyляются программными способами передачи и получения
данных от удаленных сер_jh\g_bkihevamygbq_]hdjhf_hi_jZpbc+773
Если uohlbl_ihemqblv^Zggu_kk_jера используйте HTTP GET; если u
хотите отпраblv^Zggu_gZk_jер, используйте HTTP POST. Более
продвинутые функции API HTTP _x -серbkZihaоляют создавать,
модифицироZlvbm^Zeylv^Zggu_bkihevamy+773387b+773'(/(7(
Иными словами, «глаголы» kljh| нные в HTTP протокол (GET, POST, PUT и
DELETE) могут отображаться на операции уроgyijbeh`_gby^eyihemq_gby
создания, модифицироZgbybm^Ze_gby^Zgguo.

HTTP web services are programmatic ways of sending and receiving data from
remote servers using not hing but the operations of HTTP. If you want to get data
from the server, use HTTP GET; if you want to send new data to the server, use
HTTP POST. Some more advanced HTTP web service APIs also allow creating,
modifying, and deleting data, using HTTP PUT an d HTTP DELETE. In other words,
the «verbs» built into the HTTP protocol (GET, POST, PUT, and DELETE) can map
directly to application -level operations for retrieving, creating, modifying, and
deleting data.

Главное приемущестhlZdh]hih^oh^ZwlhijhklhlZ, и эта простота оказалась
популярной. Данные — обычно XML или JSON — могут быть построены или
сохранены статически, или динамически сгенерироZgukdjbilhfgZklhjhg_
сер_jZZ\k_kh\j_f_ggu_yaudb ключая Python, конечно же!) dexqZxl
себя HTTP библио теку для их загрузки. Отладка также проста; потому что
каждый ресурс в HTTP _x -сер_k_bf__lmgbdZevgucZ^j_k \nhjf_85/ 
ufh`_l_aZ]jmablv_]h Zrеб браузер и немедленно уb^_lvkuju_
данные.

220


The main advantage of this approach is simplicity, a nd its simplicity has proven
popular. Data — usually XML or JSON — can be built and stored statically, or
generated dynamically by a server -side script, and all major programming
languages (including Python, of course!) include an HTTP library for download ing it.
Debugging is also easier; because each resource in an HTTP web service has a
unique address (in the form of a URL), you can load it in your web browser and
immediately see the raw data.

Примеры HTTP _x -серbkh:
* Google Data APIs позволяют вам взаимодействовать с широким наборо
м сервисов Google, включая Blogger и YouTube.
* Flickr Services позволяет вам загружать и выгружать фотографии на
Flickr.
* Twitter API позволяет вам публиковать обновления статусов на Twit
ter.
* и много других

Examples o f HTTP web services:
* Google Data APIs allow you to interact with a wide variety of
Google services, including Blogger and YouTube.
* Flickr Services allow you to upload and download photos from F
lickr.
* Twitter API allows you to publish status updates on Twitter.
* …and many more

Python3 располагает дmfy[b[ebhl_dZfb^eyзаимодейстbyk+773\_x -
серbkZfbKWWSFOLHQt — это низкоуроg_ая библиотека, реализующая RFC
2616 — протокол HTTP. u rllib.request — это уро_gvZ[kljZdpbbihkljh_gguc
по_joKWWSFOLHQWHgZij_^hklZляет стандартный API для доступа к HTTP и
FTP сер_jZfZтоматически следует HTTP редиректам (перенапраe_gbyf
и умеет работать с некоторыми распространенными формами HT TP
аутентификации.

Python 3 comes with two different libraries for interacting with HTTP web services:
http.client is a low -level library that implements RFC 2616 , the HTTP protocol.
urllib.request is an abstraction layer built on top of http.client. It provides a standard

221

API for accessing both HTTP and FTP servers, automatically follows HTTP
redirects, and handles some common forms of HTTP authentication.

Итак, какую же из ни следует использоZlv"GbdZdmx<f_klhgboemqr_
использоZlvKWWSOLE2 — стороннюю библиотеку с открытым кодом. Она более
полно по сраg_gbxkKWWSFOLHQWj_Zebam_l+773b то же j_fy
предостаey_lemqrmxq_f\XUOOLEUHTXHVWZ[kljZdp ию.

So which one should you use? Neither of them. Instead, you should use httplib2, an
open source third -party library that implements HTTP more fully than http.client but
provides a better abstraction than urllib.request.

Чтобы понять, почему Zrbfыбо ром должна стать httplib2, сначала нужно
понять протокол HTTP.

To understand why httplib2 is the right choice, you first need to understand HTTP.

14.2 Особенности HTTP
14.2 Features of HTTP

Есть пять Z`guohkh[_gghkl_cdhlhju_\k_+773deb_glu^he`gu
поддержиZlv.

There are five important features which all HTTP clients should support.

14.2.1 Кэширование
14.2.1 Caching

Самая Z`gZyещь ihgbfZgbbhex[hfеб -серbk_wlhlhqlh^hklmi
сеть не_jhylgh^hjh]Yg_bf_x\иду «доллары и центы» (хо тя ширина
канала не яey_lky[_kieZlghc Y]hорю о том, что для открытия
соединения, отпраdbaZijhkZbihemq_gbyhlета от удаленного сер_jZ
требуется очень много j_f_gb>Z`_gZ[ukljhfrbjhdhdZgZevghf
соединении, задержка (j_fygZi_j_^ZqmaZijhkw и началом получения
от_lguo^Zgguo fh`_lihij_`g_fm[ulvыше, чем uh`b^ZebHrb[db
роутероijhiZ`ZiZd_lh\oZd_jkdb_ZlZdbgZijhf_`mlhqgu_ijhdkb — 
интернет не быZ_lgbh^ghckihdhcghcfbgmlu , и с этим нич его нельзя
поделать.

222


The most important thing to understand about any type of web service is that
network access is incredibly expensive. I don’t mean «dollars and cents» expensive
(although bandwidth ain’t free). I mean that it takes an extraordinary lon g time to
open a connection, send a request, and retrieve a response from a remote server.
Even on the fastest broadband connection, latency (the time it takes to send a
request and start retrieving data in a response) can still be higher than you
anticipa ted. A router misbehaves, a packet is dropped, an intermediate proxy is
under attack — there’s never a dull moment on the public internet, and there may be
nothing you can do about it.

HTTP был спроектироZgkmq_lhfdwrbjhания. Есть целый класс устройст 
(т. н. «кэширующие прокси»), единст_gghcaZ^Zq_cdhlhjuoyляется
находиться между ZfbbhklZevguffbjhfbfbgbfbabjhать сетевой
трафик. В Zr_cdhfiZgbbbebmашего Интернет -проZc^_jZihqlb
на_jgydZbf_xlkydwrbjmxsb_ijhdkb^Z`__keb\uh[wlhf не знаете. Их
работа осноZgZgZdwrbjh\Zgbb\kljh_gghf протокол HTTP.

HTTP is designed with caching in mind . There is an entire class of devices (called
«caching proxies») whose only job is to sit between you and the rest of the world
and minimize network access. Your company or ISP almost certainly maintains
caching proxies, even if you’re unaware of them. They work because caching built
into the HTTP protocol.

Вот конкретный пример работы кэшироZgby . С помощью сh_]h[jZma_jZы
посетили сайт di veintomark.org. На этом сайте имеется картинка
wearehugh.com/m.jpg. Когда Zr[jZma_jaZ]jm`Z_lwlmdZjlbgdmk_j\_j
dexqZ_l\kой от_lke_^mxsb_+773aZ]hehки:

Here’s a concrete example of how caching works. You visit diveintomark.org in your
browser . That page includes a background image, wearehugh.com/m.jpg. When
your browser downloads that image, the server includes the following HTTP
headers:
HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
Last -Modified: Fri, 22 Aug 2008 04:28:1 6 GMT
ETag: "3075 -ddc8d800"
Accept -Ranges: bytes
Content -Length: 12405

223

Cache -Control: max -age=31536000, public
Expires: Mon, 31 May 2010 17:14:04 GMT
Connection: close
Content -Type: image/jpeg

224

Пример:
перенос
chardet на
PWKRQ3
Содержание
 1 Погружение
 2 Что такое аlhfZlbq_kdh_hij_^_e_gb_dh^bjhки с имheh?
o 2.1 Раз_wlhозможно?
o 2.2 Такой алгоритм сущестm_l?
 3 В_^_gb_\fh^mevFKDUGHt
o 3.1 UTF -N с МПБ
o 3.2 ЭкранироZggu_dh^bjhки
o 3.3 Многобайтныйе кодироdb
o 3.4 Однобайтные кодироdb
o 3.5 windows -1252
Погружение
Вопрос: что яey_lkyi_jой причины тарабарщины на страницах интернета, с
Zr_fihqlhом ящике, и в любой компьютерной системе когда либо
написанной? Это кодироdZkbfоло<]eZ\_ijhkljhdbyjZkkdZauал о
истории кодироhdbkha^ZgbbXgbdh{ а, "одной кодироdbijZящей всеми". Я
бы полюбил ее если бы никогда больше не b^_elZjZ[Zjsbgu _[_ihlhfm
что все системы сохраняют _jgmxbgnhjfZpbxhdh^bjhке, все протоколы

225

передачи данных поддержиZxlXgbdh^bdZ`^Zykbkl_fZjZ[hlukl_dklhf
пр идержиZeZkvb^_Zevghcерности когда кон_jlbjhала из одной
кодироdb другую.
Еще я люблю пони.
Пони юникода.
Юникодопони, так сказать.
Я останоexkvgZZтоматическом определении кодироdbkbfолов.
Что такое автоматическое определение кодировки симво лов?
Это значит aylvihke_^hательность байт в неиз_klghcdh^bjhке, и
попытаться определить кодироdmlZdqlh[u\ufh]ebqblZlvl_dklWlhdZd
aehfZlvdh]^Zdh]^Zmас нет ключа для расшифроdb.
Разве это возможно?
Вообще, да. Однако, некоторые кодироdbhilbfbabjhанны для конктерных
языкоbyaudbg_kemqZcguG_dhlhju_ihke_^hательности симheh\
klj_qZxlkyihklhygghdh]^Z^jm]b_hq_gvj_^dbQ_eh\_dkihdhcgh
гоhjysbcihZg]ebckdbhldjuая газету и найдя там “txzqJv 2!dasd0a QqdKjvz”
сразу определит что это не английский(даже если состоит полностью из
английских букв). При помощи изучения большого количестZh[uqgh]h
текста, компьютерный алгоритм может имитироZlvagZgb_yaudZbh[mqblvky
делать предположения о языке текста.
Другими слова ми, определение кодироdbwlhgZkZfhf^_e_hij_^_e_gb_
языка, объедененное со знанием какой язык использоZlvkdZdhcdh^bjhкой.
Такой алгоритм существует?
Черт побери да! Все осноgu_[jZma_jubf_xlстроенное автоопределение
кодироdbihkdhevdmbgl_jg ет полон страниц на которых кодироdZообще не
указана. Mozilla Firefox dexqZ_l[b[ebhl_dmZтоопределения кодироdb
симоheh с открытым исходным кодом. Я портировал ее на Python2 и
продублироZe модуле chardet. Эта глаZihdZ`_l\ZfihrZ]h\hесь
про цесс переноса модуля chardet с Python 2 до Python 3.
Введение в модуль chardet
Прежде чем начать переносить код, я помогу ZfihgylvdZdwlhldh^jZ[hlZ_l
Это краткий инструктаж по наb]Zpbb исходном коде. Библиотека chardet
слишком болшьшая чтобы dexq ать ее здесь полностью, но uih`_l_kdZqZlv
ее с chardet.feedparser.org.
Точкой oh^ZZe]hjblfZhij_^_e_gbyyляется universaldetector.py, в котором
есть один класс UniversalDetector. (Вы могли подумать что точка oh^Zwlh

226

функция detect в chardet/__init_ _.py, но на самом деле это просто удобная
функция которая создает объект UniversalDetector, uauает его, и
haращает его результат)
Вот пять категорий кодироhddhlhju_ih^^_j`bает UniversalDetector:
 UTF -N с меткой порядка байт( МПБ). Это dexqZ_l\k_[y87F -8, оба
большой -индийский и малый -индийский ZjbZgl87F -16, и k_
ZjbZglZ87F -32 заbkysbohlihjy^dZ[Zcl.
 ЭкранироZggu_ihke_^hательности, которые полностью соf_klbfuk
7-битным ASCII, где симheug_ходящие k_fb[blg ую кодироdm$6&,,
начинаются с симheZwdjZgbjhания. Например: ISO -2022 -JP(Японская)
и HZ -GB -2312(Китайская)
 Многобайтоu_dh^bjhки, где каждый симheij_^klZлен различным
количестhf[ZclGZijbf_j%,* DblZckdZy 6+,)7B-,6 YihgkdZy b
TIS -620(Тайс кая)
 Однобайтоu_dh^bjhки, где каждый симheij_^klZлен одним
байтом. Нарпимер: KOI8 -R(Русская), WINDOWS -1266(Иjbl b7,S -
620(Тайская)
 WINDOWS -1252, которая в основном используется 0LFURVRIW:LQGRZV
менеджерами среднего з_gZdhlhju_g_ohlylaZ^mfu Zlvkyh
кодироd_kbfолоkb^y сh_cghj_.
UTF -N с МПБ
Если текст начинается с МПБ, мы можем разумно заключить что текст
закодироZgijbihfhsbdh^bjhки UTF -8, UTF -16, или UTF -32. (МБП
расскажет нам какой именно; именно для этого она и служит.) Это
по ддержиZ_lky\8QLYHUVDO'HWHFWRUdhlhjucернет результат сразу без
каких -либо дальнейших изысканий.
Экранированные кодировки
Если текст содержит распознаZ_fmxwdjZgbjhанную последоZl_evghklvlh
это может быть индикатором экранироZgghcdh^bjh\db8QLv ersalDetector
создаст EscCharSetProber(определенный HVFSUREHUS\ bhl^Zkll_dklgZ
обработку.
EscCharSetProber создаст ряд конечных аlhfZlh\hkghанных на моделях
HZ -GB -2312, ISO -2022 -CN, ISO -2022 -JP, и ISO -2022 -KR (определенных 
escsm.py). EscCharSe tProber пропустит текст через каждый конечный аlhfZl
побайтоh?keblhevdhh^bgbaZтомато^Zkliheh`bl_evgucj_amevlZl
про_jdb(VF&KDU6HW3UREHUg_aZf_^ebl_evghернет его в UniversalDetector,
который, в сhxhq_j_^vhl^Zkl_]hызZ\r_fm_]hijhp| ссу. Если любой из
конечных аlhfZlh наткнется на недопустимую последовательность, он
останаebается и далее продолжается обработка при помощи другого
конечного аlhfZlZ.

227

Многобайтныйе кодировки
Осноu\ZykvgZFI;8QLYHUVDO'HWHFWRUijhеряет содержит ли текст симheu
со старшим байтом. Если так, то он создает набор «исследоZl_e_c^ey
определения многобайтных кодировок, однобайтных кодироhdb качест_
последнего средстZZLQGRZs -1252.
ИсследоZl_ev^eyfgh]h[Zclgucodh^bjhок,
MBCSGroupProber(опреде ленный в mbcsgroupprober.py), на самом деле
просто консоль которая упраey_l]jmiihc^jm]bobkke_^hателей, по одному
на каждую многобайтную кодироdm%LJ*%(8C -TW, EUC -KR, EUC -JP,
SHIFT_JIS, и UTF -8. MBCSGroupProber отдает текст каждому из этих
кодироdhaZисимых исследоZl_e_cbijhеряет результат. Если
исследоZl_evkhh[sZ_lqlhgZr_eg_^himklbfmxihke_^hательность, он
исключается из дальнейшей обработки(так что любые последующие uahы
UniversalDetector.feed() пропустят этого исследователя). Если исследователь
сообщает что он достаточно у_j_g том что определил кодироdm
MBCSGroupProber сообщает о положительном результате в UniversalDetector,
который передает результат uaавшему его процессу.
Большинстhbkke_^hателей многобайтных кодиров ок наследоZguhl
MultiByteCharSetProber(определенном PEFKDUVHWSUREHUS\ bijhklh
dexqZxl\k_[yih^oh^ysbcdhg_qgucZтомат и анализатор распределения
а остальную работу uihegy_l0XOWL%\WH&KDU6HW3UREHU0XOWL%\WH&KDU6HW3UREHU
пропускает текст через з аbkbfuchldh^bjhки конечный автомат побайтно,
чтобы найти последовательность байт которая бы указала на положительный
или отрицательный результат. В то же j_fy0XOWL%\WH&KDU6HW3UREHU
пропускает текст через заbkbfuchldh^bjhки анализатор распределен ия.
Анализатор распределения(каждый определен в chardistrubution.py)
использует модель dhlhjhcmdZaZgh каком языке какие симheu
klj_qZxlkyqZs_DZdlhevdh0XOWL%\WH&KDU6HW3UREHUhl^Ze^hklZlhqghl_dklZ
для анализа, uqbkey_lkyj_clbg]koh`_klbhkghy анный на числе часто
используемых симheh, общем количест_kbfолоbdhwnnbpb_gl_
распределения специфичном для языка. Если у_j_gghklv^hklZlhqgh\ukhdZ
MultiByteCharSetProber haращает результат в MBCSGroupProber, который
haращает результат в Uni versalDetector, а он в сhxhq_j_^v\uaавшему
его процессу.
Тяжелее k_]hjZah[jZlvkykyihgkdbfH^ghkbfольного анализатора
распределения не k_]^Z^hklZlhqghqlh[ujZaebqblv(8C -JP и SHIFT_JIS,
поэтому SJISProber(определенный VMLVSUREHUS\ lZd`_bki ользует
дmokbfольный анализатор распределения. SJISContextAnalysis и
EUCJPContextAnalysis (оба определенные в jpcntx.py и оба наследоZggu_hl
класса JapaneseContextAnalysis) про_jyxlqZklhlmihторения симheh
Хироганы в тексте. Как только достаточно текста было обработано, он
haращает уро_gvmеренности в SJISProber, который про_jy_lh[Z
анализатора и haращает результат на уро_gvыше 0%&6*URXS3UREHU.

228

Однобайтные кодировки
Серьезно, где мой пони юникода?
ИсследоZl_ev^eyh^gh[Zclguodh^bjhо к, SBCSGroupProber(определенный
VEFVJURXSSUREHUS\ lZd`_ijhklhdhgkhevdhlhjZymijZляет группой
исследоZl_e_cihh^ghfmgZdZ`^mxdhf[bgZpbxh^gh[Zclghcdh^bjhки и
языка: windows -1251, KOI8 -R, ISO -8859 -5, MacCyrillic, IBM855, и IBM866
(Русский); ISO -8859 -7 и windows -1253 (Греческий); ISO -8859 -5 и windows -1251
(Болгарский); ISO -8859 -2 и windows -1250 (Венгерский); TIS -620 (Тайский);
windows -1255 и ISO -8859 -8 (Иjbl .
SBCSGroupProber отдает текст каждому такому исследователю специфичному
для языка и кодироdbbijhеряет результат. Все эти исследоZl_eb
реализоZgudZdh^bgdeZkk6LQJOH%\WH&KDU6HW3UREHU hij_^_e_gguc
sbcharsetprober.py), который принимает модель языка dZq_klе аргумента.
Модель языка определяет как часто klj_qZxlkyjZaebqgu_^у хсимhevgu_
последоZl_evghklb\h[uqghfl_dkl_6LQJOH%\WH&KDU6HW3UREHUh[jZ[Zluает
текст и отмечает наиболее часто используемые дmokbfольные
последоZl_evghklbDZdlhevdh[uehh[jZ[hlZgh^hklZlhqghl_dklZhg
uqbkey_lmjhень схожести основанный на количестве часто встречающихся
последоZl_evghkl_ch[s_fdhebq_klе симheh, и специфичным для языка
коэффициентом распределения.
Иjblh[jZ[Zluается по особому. Если при помощи анализа
дmokbfольного распределения uykgy_lkyqlhl_dklfh`_l[ulvgZBy рите,
HebrewProber(определенный KHEUHZSUREHUS\ ijh[m_ljZaebqblv
Визуальный Иjbl ]^_dZ`^ZykljhdZbkoh^gh]hl_dklZojZgblkygZh[hjhlb
потом отображается так же чтобы она могла быть прочтена с праZgZe_о) и
Логический Иjbl ]^_l_dklkhojZg_gy порядке чтения и после этого
отображается в клиенте справа на леh Ihkdhevdmg_dhlhju_kbfолы
кодируются различно в заbkbfhklbhliheh`_gby слове, мы можем сделать
обосноZggh_ij_^iheh`_gb_hgZijZлении исходного текста, и определить
нужную кодиро dm ZLQGRZs -1255 для Логического ИjblZbeb,6O -8859 -8 для
Визуального ИjblZ)
windows -1252
Если UniversalDetector определяет симheukhklZjrbf[Zclhf тексте, но ни
один из других многобайтныйх или однобайтный исследователей не _jgme
положительный резу льтат, создается Latin1Prober(определенный в
latin1prober.py) чтобы попытаться определить английский текст dh^bjh\d_
windows -1252. Это будет изначально не надежным анализом, потому что
английские симheuaZdh^bjhаны таким же способом как и во многих
раз личных кодироdZo?^bgklенный способ определить windows -1252 это
обратить внимание на часто используемые симheudZdmfgu_dZычки,
vxsb_kyZihkljhnukbfолы копирайта и т. д. Latin1Prober аlhfZlbq_kdb

229

уменьшает сhcmjhень у_j_gghklbqlh[u^jm]b_ более досто_jgu_
исследоZl_ebfh]ebыиграть если hafh`gh.

Создание
пакето
библиотек »

Перенос кода
на PWKRQ с
помощью 2to3»

230

Особые
названия
методов

Мы уже обнаружили несколько специальных наименоZgbcf_lh^h поkx^m
книге — магические методы которые питон uauает когда ubkihevam_l_
определенный синтаксис. Используя специальные методы ZrbdeZkkufh]ml
работать как последовательности, как словари, как функции, как итераторы
или даже как числа. Аппендикс служит как спраhqgbdihki_pbZevguf
методам которые мы уже b^_ebbdhjhldh__^_gb_ некоторые более
эзотерические из них.
Основы
Если uqblZebведение в классы вы уже b^_ebkZfu_h[sb_ki_pbZevgu_
методы: метод __init__ (). Многообразие классов которые я написал требуют
некоторой инициализации. Также есть некоторые другие специальные методы
которые особенно полезны для отлажиZgbyаших пользоZl_evkdbodeZkkh.
1. Метод __init__ () вызыZ_lkyihke_lh]hdZdwda_fieyjkha^Zg?kebы
хотите контролироZlvijhp_kkkh~ дания используйте метод __new__ ().
2. По соглашению метод __repr__ () должен haращать строку которая
яey_lky^_cklительным питоновским ujZ`_gb_f.
3. Метод __str__ () также uau\Z_lkydh]^Zbkihevam_lky print (x).
4. Ноh_ Python3, был еден ноuclbiE\WHV.
5. По соглашению, format_spec должен удовлетhjylv)RUPDW6SHFLILFDWLRQ
Mini -Language decimal.py klZg^Zjlghc[b[ebhl_d_3\WKRQ которой
имееться сhcf_lh^ __format__ ().
Классы, которые ведут себя как итераторы.

231

В гла_ijhbl_jZlhjuы b^_ebdZdihkljhbl ь итератор с нуля используя
методы __iter__ () и __next__ ().
1. Метод __iter__ () вызыZ_lvkydh]^Zы создаете ноucbl_jZlhjWlh
хорошее место для инициализации итератора начальными значениями.
2. Метод __next__ () uauаеться когда uihemqZ_l_ke_^mxs__agZq_g ие
из итератора.
3. Метод __reversed__ () яey_lkyHgihemqZ_lkms_kl\mxsmx
последоZl_evghklvbозвращает итератор который произh^bl
элементы в последовательности в обратном порядке, от последнего к
перhfm.
Как uидели ]eZ\_Bl_jZlhjupbdeIRUfh`_l быть применен к итератору.
В этом цикле:
for x in seq :
print (x)
Python 3 будет uauать seq. __iter__ () для создания итератора, затем вызо_l
метод __next__ () для этого итератора для получения каждого значения х.
Когда метод __next__ ()

Куда пойти

Это стоит прочитать
Сущестm_lg_dhlhjh_dhebq_kl\hl_fg_^hklZlhqghjZkdjuluo этой книге,
однако для их раскрытия сущестmxlhldjulu_j_kmjku.
Декораторы:
 Декораторы Функций от Ariel Ortiz
 Подробнее о Декораторах Функций от Ariel Ortiz
 Очаровательный Python: Магия Декораторов это просто от David Mertz
 Определение Функций hnbpbZe ьной документации Python
Сhcklа:

232

 The Python property builtin от Adam Gomaa
 Getters/Setters/Fuxors от Ryan Tomayko
 prope rty() функция в официальной документации Python
Дескрипторы:
 How -To руководство по Дескрипторам от Raymond Hettinger
 Очаровательный Python: Элегантность и недостатки Python, Часть 2 от David
Mertz
 Дескрипторы Python от Mark Summerfield
 Вызов Дескрипторов hnbpbZevghc^hdmf_glZpbb3\WKRn
Мультипотоковость & многопроцессорность:
 threading модуль
 threading Управление конкурирующими потоками
 multiprocessing модуль
 multiprocessing Управление процессами как потоками
 Потоки Python и Global Interpreter Lock от Jesse Noller
 Внутри Python GIL ( видео ) от David Beazley
Метаклассы:
 Программирование метаклассов в Python от David Mertz and Michele
Simionato
 Программирование метаклассов в Python, Часть 2 от David Mertz and Michele
Simionato
 Программирование метаклассов в Python, Часть 3 от David Mertz and Michele
Simionato
И ^hiheg_gb_'RXJ+HOOPDQ Python Модуль недели , это фантастическое
рукоh^klо к большинству модулей для стандартной библиотки Python.
Где искать совместимый с Python 3 код.
Так как Python 3 относительно новый, сущестm_l^h\hevghfZehkhy местимых
библиотек. Вот некоторые из мест, где Вы могли бы их отыскать.
 Python Package Index: список пакетов Python 3
 Python Cookbook: список рецептов для Python 3
 Google Project Hosting: список проектов Python3
 SourceForge: список проектов базирующихся на Python 3
 GitHub: список проектов Python 3 (а также, список проектов Python 3 )
 BitBucket: список проектов python3 (а также Python 3 )

233

Устранение
проблем

Использование командной строки
Во j_fyql_gbywlhcdgb]bы b^_ebijbf_ju\dhlhjuoijh]jZffZ
запускалась из командной строки. Но откуда ее взять? В Linux, найдите f_gx
Приложений ( Applications ) программу под нозZgb_fL_jfbgZe( Terminal ). (Эта
программа может быть и в каком -либо из подменю) В Mac OS X, ufh`_l_
найти Терминал ( Terminal ) в папке /Applications/Utilities/ . Чтобы зайти g__
кликните по рабочему столу, откройте меню Перейти (Go ), u[_jbl| Перей ти в
папку … ( Go to folder... ) и едите /Applications/Utilities/ . Затем, дZ`^udebdgbl_
по программе Терминал ( Terminal ). В Windows, нажмите Пуск, u[_jbl_imgdl
меню Выполнить… ( Run ), едите cmd и нажмите ENTER .
Запуск программ из командной строки
Как только uaZimklbebdhfZg^gmxkljhdmы получаете hafh`ghklv
запустить интерактиgmxh[hehqdm3\WKRn . В терминале Linux или Mac OS X
наберите python3 и нажмите ENTER . В командной строке Windows едите
c: \python31 \python и нажмите ENTER . Если все про йдет удачно, то umидите
что -то, похожее на это
you@localhost:~$ python3
Python 3.1 (r31:73572, Jul 28 2009, 06:52:23)
[GCC 4.2.4 (Ubuntu 4.2.4 -1ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
(В_^bl_ exit() и нажмите ENTER , чтобы закрыть интерактивную оболочку
Python и _jgmlvky командную строку. Это работает hсех операционных
системах.)
Если uидите ошибку "command not found" ("Команда не найдена") , то это
может означать, что Python 3 не устано e_g.

234

you@localhost:~$ python3
bash: python3: command not found

О книге

«Погружение 3\WKRQключает оригинальные тексты и изображения,
распространяемые на условиях лицензии CC -BY -SA 3.0 [1] . Иллюстрации из
Open Clip Art Library [2] яeyxlky общест_gguf^hklhygb_f .
Библиотека распространяется на условиях LGPL 2.1 или более chardet
ihag_c_jkbbJ_rZl_evdjbilZjbnfh\ — порт Эттингера программы Рэймонда
(Raymond Hettinger), выпущенной под лицензией MIT . Некоторые глаu
содержат код из стандартной библиотеки Python, uims_gghcih^ebp_gab_c
PSF 2.0. Все остальные оригинальные исходные коды распространяются под
лицензией MIT.
Сайт использует jQuery [3] , uims_ggmxih^ diveintopython3.org
ebp_gabyfb0,7b GPL . Расц_ldZkbglZdkbkZ\hjb]bgZevghfl_dkl_ ghg_\
этом переh^_ ijhbaодилась при помощи prettify.js и highlighter.js ; обе
библиотеки uims_guih^ebp_gab_c Apache License 2.0 .
Испраe_gbybhlauы по оригинальному тексту посылайте на
mark@diveintomark.org. Замечания по переh^m — Сыру Российскому.

О переводе

Участники
 Сыр Российский (координатор)
 9e9names
 Blogytalky

235

 Bobry
 Ichiro
 K0sh
 Ls
 Pumbatwarek
Благодарности
 МножестmZghgbfguoi_j_одчиков.
Трудности перевода
Терпение, кузнечик
http://yourneedtoknow.com/self -improvement/patience -grasshopper/
Постижение генератора
Мне кажется, в гла_ijhFRPSUHKHQVLRQV (генераторы) Марк взял первый
попаrbckyZnhjbafkhkeh\hfFRPSUHKHQG ihklb]Zlv Ijbi_j_оде
игру слов hkijhbaести не удалось, urehdZd -то совсем ни рыба ни
мясо. // Сыр Российский 09:50, 4 января 2012 (UTC)

Выходные
данные

Je n’ai fait celle Jci plus longue que parce que je n’ai pas eu le loisir de la faire plus
courte. ~ Это письмо получилось таким длинным потому, что у меня не было
j_f_gbgZibkZlv_]hdhjhq_. =
Блез Паскаль

236

Примечание переh^qbdZAgZqbl_evgZyqZklvwlhck траницы
относится только к оригинальной английской HTML -_jkbb.
Погружение
Эта книга, как и все книги, сделана с любовью. Конечно, я получил немножко
баксов за неё, но никто не пишет техническую литературу ради денег. И
поскольку эта книга доступна как на бумаге, так и на вебсайте, я потратил кучу
j_f_gbgZ\kydb_ебовские шт учки f_klhlh]hqlh[uibkZlv.

Онлайн -версия загружается максимально
эффективно. Эффективность никогда не
приходит сама, я потратил на неё много
часов. Может быть, слишком много часов.
Да, почти на_jgydZkebrdhffgh]h
часов. Никогда не недооцениZcl_
глубину того болота, которое затягиZ_l
писателей, откладыZxsbojZ[hlm
долгий ящи к.
Не буду надоедать Zfi_j_qbke_gb_f
k_o^_lZe_cG_lih]h^bl_Y[m^m
надоедать Zfi_j_qbke_gb_f\k_o^_lZe_cGhihdZот ZfdhjhldZyерсия.
1. HTML -код сокращён, и при u^Zq_ сжимается .
2. Скрипты и стили сокращены при помощи YUI Compressor [1] (и тоже
u^Zxlky\k`Zlhfиде).
3. Скрипты собраны f_kl_qlh[umf_gvrblvdhebq_kl\haZijhkh\ HTTP .
4. Стили собраны f_kl_bqZklbqgh встроены в текст, чтобы уменьшить
количестhaZijhkh HTTP.
5. Неиспользуемые селекторы и сhcklа CSS постранично удалены при
небольшой помощи pyq uery .
6. HTTP -кэширование и прочие сер_jgu_hipbbhilbfbabjhаны на
основе рекомендаций YSlow [2] и Page Speed .
7. Где hafh`ghместо изображений исполь зуются юникодные симheu
[3] .
8. Изображения оптимизироZgukihfhsvx OptiPNG [4] .
9. Вся книга была с любовью написана руками на HTML 5, чтобы не было
мусора в коде разметки.
Типографика
_jlbdZevgucjblfkZfucdjZkbый амперсанд, фигурные кавычки и
апострофы и ещё много чего с webtypography.net

237

Графика
Юникод , ughkdbjZ[hlZgZ^IRQt -family :LQGRZs
Быстродействие
«Dive Into History 2009 edition», с минимизацией CSS + JS + HTML,
kljh_ggufb&66khilbfbaZpb_cbah[jZ`_gbc
Забавные штуки
Цитаты, неровный почерк (?), [1] МуссИзПапайи (PapayaWhip)
Материалы для дальнейшего чтения
 Применение типографских элементов в Вебе
 Привязка шрифта к линиям сетки в Вебе
 Набор текста с соблюдением вертикального ритма
 Использование самого красивого амперсанда
 Поддержка Юникода в HTML, шрифтах и веб -браузерах
 YSlow [5] для Firebug [6]
 Рекомендации по ускорению веб -сайтов
 14 правил быстрой загрузки веб -сайтов
 YUI Compressor [7]
 Google Page Speed
 Искпользование Google Page Speed
 OptiPNG [8]
Примечания
1. Вопросительный знак принадлежит аlhjmjZkrbnjhывайте его сами.
А «constrained writing» может быть не только «неровным почерком», но и
«напряжённым стилем изложения». Смысл несильно, но меняется. —
Прим. пер.

Сообщить о нарушении / Abuse

Все документы на сайте взяты из открытых источников, которые размещаются пользователями. Приносим свои глубочайшие извинения, если Ваш документ был опубликован без Вашего на то согласия.