Разное

Погружение в python 3: Погружение в Python 3

Содержание

Что нового в «Погружении в Python 3» — Погружение в Python 3

Isn’t this where we came in?

Pink Floyd, The Wall

или «минус первый уровень»

Вы прочитали «Погружение в Python» и, может быть, даже купили бумажную версию. (Спасибо!) Вы уже неплохо знаете Python 2. Вы готовы окунуться с головой в Python 3… Если всё это про вас, читайте дальше. (Если что-либо из этого неверно, вам следует начать с начала.)

Вместе с Python 3 поставляется скрипт под названием 2to3. Изучите его. Полюбите его. Используйте его. «Перенос кода на Python 3 с помощью 2to3» — справочник по всем тем изменениям, которые 2to3 может проделать автоматически. Поскольку многое из этого — изменения в синтаксисе, лучше всего начать именно с них. (print теперь функция, `x` не работает и т. д.)

«Пример: перенос chardet на Python 3» рассказывает о моей (в конце концов, успешной) попытке перенести одну нетривиальную библиотеку с Python 2 на Python 3. Это может вам помочь. А может и не помочь. Кривая обучения получается довольно крутой из-за того, что вам сначала необходимо хоть немного разобраться в этой библиотеке, чтобы понимать, что именно в ней поломалось и как я это исправил. Часто программы ломаются на строках (strings). Кстати, о строках…

Строки… Ох… С чего бы начать?… В Python 2 были строки (str) и юникодные строки (unicode). В Python 3 — байты (bytes) и строки (string). То есть все строки стали теперь юникодными, а когда хотите работать с кучей байтов, вы используете новый тип bytes. Python 3 никогда автоматически не преобразовывает строки в байты или наоборот, поэтому, если вы не уверены, что из них вы имеете в какой-то определённый момент, ваш код почти наверняка поломается. В главе о строках эта ситуация разбирается подробнее.

Байты и строки ещё не раз будут появляться в книге.

  • В главе «Файлы» вы узнаете разницу между чтением файлов в «двоичном» и «текстовом» режимах. Чтение (и запись!) файлов в текстовом режиме требует указание параметра encoding (кодировка). Одни файловые методы при работе с текстовыми файлами подсчитывают символы, другие считают байты. Если в вашей программе предполагается, что один символ == одному байту, она будет падать на многобайтовых символах.
  • В главе «HTTP и веб-сервисы» модуль httplib2 принимает заголовки и данные по HTTP. Заголовки HTTP возвращаются как строки, а тело ответа HTTP возвращается в виде байтов.
  • В главе «Сериализация объектов Python» вы узнаете, почему модуль pickle в Python 3 определяет новый формат данных, несовместимый с Python 2. (Подсказка: это из-за различий между байтами и строками.) Ещё есть JSON, который вовсе не поддерживает тип bytes. Я покажу вам, как справиться с этим ограничением.
  • Глава «Пример: перенос chardet на Python 3» — просто кровавое месиво из байтов и строк.

Даже если вы не задумываетесь о Юникоде (рано или поздно всё равно придётся), вы захотите прочитать о форматировании строк в Python 3, оно совершенно отличается от такового в Python 2.

Итераторы в Python 3 везде, и сейчас я разбираюсь в них намного лучше, чем пять лет назад, когда я писал «Погружение в Python». Вам тоже необходимо разбираться в них, потому что множество функций, которые в Python 2 возвращали списки, теперь в Python 3 возвращают итераторы. Как минимум, следует прочитать вторую половину главы «Итераторы» и вторую половину главы «Подробнее об итераторах».

По многочисленным просьбам я добавил приложение «Особые названия методов», которое немного похоже на главу «Модель данных» документации по Python, только с приколами.

Когда я писал «Погружение в Python», все доступные на тот момент библиотеки для работы с XML были жутким отстоем. Впоследствии Фредрик Лунд (Fredrik Lundh) написал ElementTree, которая уже совсем не отстой. Питоновские боги, показав свою мудрость, внедрили ElementTree в стандартную библиотеку, и теперь она составляет основу моей новой главы про XML. Старые способы разбора XML всё ещё доступны, но их надо избегать, потому что они отстойные!

Ещё из нового в Python — не в языке, а в сообществе — появление репозиториев вроде The Python Package Index (PyPI). К Python прилагаются утилиты для пакетирования вашего кода в стандартных форматах и дальнейшего его распространения через PyPI. Подробнее в главе «Пакетирование библиотек Python».

Ваша первая программа на Python — Погружение в Python 3

Не убегайте от проблем, не осуждайте себя и не несите своё бремя в праведном безмолвии. У вас есть проблема? Прекрасно! Это пойдёт на пользу! Радуйтесь: погрузитесь в неё и исследуйте!

Досточтимый Хенепола Гунаратана

Погружение

Обычно книги о программировании начинаются с кучи скучных глав о базовых вещах постепенно переходят к созданию чего-нибудь полезного. Давайте всё это пропустим. Вот вам готовая, работающая программа на Python. Возможно, вы ровным счётом ничего в ней не поймёте. Не беспокойтесь об этом, скоро мы разберём её строчка за строчкой. Но сначала прочитайте код и посмотрите, что вы сможете извлечь из него.

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):
    »’Преобразует размер файла в удобочитаемую для человека форму.

    Ключевые аргументы:
    size — размер файла в байтах
    a_kilobyte_is_1024_bytes — если True (по умолчанию), используются степени 1024
                                если False, используются степени 1000

    Возвращает: текстовую строку (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 это будет выглядеть примерно так:

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

Что сейчас произошло? Вы выполнили свою первую программу на Python. Вы вызвали интерпретатор Python в командной строке и передали ему имя скрипта, который хотели выполнить. В скрипте определена функция approximate_size(), которая принимает точный размер файла в байтах и вычисляет «красивый» (но приблизительный) размер. (Возможно, вы видели это в Проводнике Windows, в Mac OS X Finder, в Nautilus, Dolphin или Thunar в Linux. Если отобразить папку с документами в виде таблицы, файловый менеджер в каждой её строке покажет иконку, название документа, размер, тип, дату последнего изменения и т. д. Если в папке есть 1093-байтовый файл с названием «TODO», файловый менеджер не покажет «TODO 1093 байта»; вместо этого он скажет что-то типа «TODO 1 КБ». Именно это и делает функция approximate_size().)

Посмотрите на последние строки скрипта, вы увидите два вызова print(approximate_size(аргументы)). Это вызовы функций. Сначала вызывается approximate_size(), которой передаётся несколько аргументов, затем берётся возвращённое ею значение и передаётся прямо в функцию print(). Функция print() встроенная, вы нигде не найдёте её явного объявления. Её можно только использовать, где угодно и когда угодно. (Есть множество встроенных функций, и ещё больше функций, которые выделены в отдельные модули. Терпение, непоседа.)

Итак, почему при выполнении скрипта в командной строке, всегда получается один и тот же результат? Мы ещё дойдём до этого. Но сначала давайте посмотрим на функцию approximate_size().

Объявление функций

В Python есть функции, как и в большинстве других языков, но нет ни отдельных заголовочных файлов, как в C++, ни конструкций interface/implementation, как в Паскале. Когда вам нужна функция, просто объявите её, например, так:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):

Когда вам нужна функция, просто объявите её.

Объявление начинается с ключевого слова def, затем следует имя функции, а за ним аргументы в скобках. Если аргументов несколько, они разделяются запятыми.

К тому же, стоит заметить, что в объявлении функции не задаётся тип возвращаемых данных. Функции в Python не определяют тип возвращаемых ими значений; они даже не указывают, существует ли возвращаемое значение вообще. (На самом деле, любая функция в Python возвращает значение; если в функции выполняется инструкция return, она возвращает указанное в этой инструкции значение, если нет — возвращает None — специальное нулевое значение.)

В некоторых языках программирования функции (возвращающие значение) объявляются ключевым словом function, а подпрограммы (не возвращающие значений) — ключевым словом sub. В Python же подпрограмм нет. Все функции возвращают значение (даже если оно None), и всегда объявляются ключевым словом def.

Функция approximate_size() принимает два аргумента: size и kilobyte_is_1024_bytes, но ни один из них не имеет типа. В Python тип переменных никогда не задаётся явно. Python вычисляет тип переменной и следит за ним самостоятельно.

В Java и других языках со статической типизацией необходимо указывать тип возвращаемого значения и каждого аргумента функции. В Python явно указывать тип, для чего-либо, не нужно. Python самостоятельно отслеживает типы переменных на основе присваиваемых им значений.

Необязательные и именованные аргументы

В Python аргументы функций могут иметь значения по умолчанию; если функция вызывается без аргумента, то он принимает своё значение по умолчанию. К тому же, аргументы можно указывать в любом порядке, задавая их имена.

Давайте ещё раз посмотрим на объявление функции approximate_size():

def approximate_size(size, a_kilobyte_is_1024_bytes=True):

Второй аргумент — a_kilobyte_is_1024_bytes — записывается со значением по умолчанию True. Это означает, что этот аргумент необязательный; можно вызвать функцию без него, а Python будет действовать так, как будто она вызвана с True в качестве второго параметра.

Теперь взглянем на последние строки скрипта:

if __name__ == ‘__main__’:
    print(approximate_size(1000000000000, False))  ①
    print(approximate_size(1000000000000))         ②

Функция approximate_size() вызывается с двумя аргументами. Внутри функции approximate_size() переменная a_kilobyte_is_1024_bytes будет False, поскольку False передаётся явно во втором аргументе.
Функция approximate_size() вызывается только с одним аргументом. Но всё в порядке, потому что второй аргумент необязателен! Поскольку второй аргумент не указан, он принимает значение по умолчанию True, как определено в объявлении функции.

А ещё можно передавать значения в функцию по имени.

>>> 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)  ③
‘4.0 KB’
>>> approximate_size(a_kilobyte_is_1024_bytes=False, 4000)       ④
  File «<stdin>», line 1
SyntaxError: non-keyword arg after keyword arg
>>> approximate_size(size=4000, False)                           ⑤
  File «<stdin>», line 1
SyntaxError: non-keyword arg after keyword arg

Перевод сообщений оболочки:

  Файл «<stdin>», строка 1
SyntaxError: неименованный аргумент после именованного
Функция approximate_size() вызывается со значением 4000 в первом аргументе и False в аргументе по имени a_kilobyte_is_1024_bytes. (Он стоит на втором месте, но это не важно, как вы скоро увидите.)
Функция approximate_size() вызывается со значением 4000 в аргументе по имени size и False в аргументе по имени a_kilobyte_is_1024_bytes. (Эти именованные аргументы стоят в том же порядке, в каком они перечислены в объявлении функции, но это тоже не важно.)
Функция approximate_size() вызывается с False в аргументе по имени a_kilobyte_is_1024_bytes и 4000 в аргументе по имени size. (Видите? Я же говорил, что порядок не важен.)
Этот вызов не работает, потому что за именованным аргументом следует неименованный (позиционный). Если читать список аргументов слева направо, то как только встречается именованный аргумент, все следующие за ним аргументы тоже должны быть именованными.
Этот вызов тоже не работает, по той же причине, что и предыдущий. Удивительно? Ведь сначала передаётся 4000 в аргументе по имени size, затем, «очевидно», можно ожидать, что False станет аргументом по имени a_kilobyte_is_1024_bytes. Но в Python это не работает. Раз есть именованный аргумент, все аргументы справа от него тоже должны быть именованными.

Написание читаемого кода

Не буду растопыривать перед вами пальцы и мучить длинной лекцией о важности документирования кода. Просто знайте, что код пишется один раз, а читается многократно, и самый важный читатель вашего кода — это вы сами, через полгода после написания (т. е. всё уже забыто, и вдруг понадобилось что-то починить). В Python писать читаемый код просто. Используйте это его преимущество и через полгода вы скажете мне «спасибо».

Строки документации

Функции в Python можно документировать, снабжая их строками документации (англ. documentation string, сокращённо docstring). В нашей программе у функции approximate_size() есть строка документации:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    »’Преобразует размер файла в удобочитаемую для человека форму.

    Ключевые аргументы:
    size — размер файла в байтах
    a_kilobyte_is_1024_bytes — если True (по умолчанию), используются степени 1024
                                если False, используются степени 1000

    Возвращает: текстовую строку (string)

    »’

Каждая функция заслуживает хорошую документацию.

Тройные кавычки[1] используются для задания строк[2] содержащих многострочный текст. Всё, что находится между начальными и конечными кавычками, является частью одной строки данных, включая переводы строк, пробелы в начале каждой строки текста, и другие кавычки. Вы можете использовать их где угодно, но чаще всего будете встречать их в определениях строк документации.

Тройные кавычки — это ещё и простой способ определить строку, содержащую одинарные (апострофы) и двойные кавычки, подобно qq/…/ в Perl 5.

Всё, что находится в тройных кавычках, — это строка документации функции, описывающая, что делает эта функция. Строка документации, если она есть, должна начинать тело функции, т. е. находится на следующей строчке сразу под объявлением функции. Строго говоря, вы не обязаны писать документацию к каждой своей функции, но всегда желательно это делать. Я знаю, что вам уже все уши прожужжали про документирование кода, но Python даёт вам дополнительный стимул — строки документации доступны во время выполнения как атрибут функции.

Многие IDE для Python используют строки документации для отображения контекстной справки, и когда вы набираете название функции, её документация появляется во всплывающей подсказке. Это может быть невероятно полезно, но это всего лишь строки документации, которые вы сами пишете.

Путь поиска оператора import

Перед тем, как идти дальше, я хочу вкратце рассказать о путях поиска библиотек. Когда вы пытаетесь импортировать модуль (с помощью оператора import), Python ищет его в нескольких местах. В частности, он ищет во всех директориях, перечисленных в sys.path. Это просто список, который можно легко просматривать и изменять при помощи стандартных списочных методов. (Вы узнаете больше о списках в главе Встроенные типы данных.)

>>> 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                                                        ③
<module ‘sys’ (built-in)>
>>> 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’]

Импортирование модуля sys делает доступными все его функции и атрибуты.
sys.path — список имён директорий, определяющий текущий путь поиска. (Ваш будет выглядеть иначе, в зависимости от вашей операционной системы, от используемой версии Python и от того, куда он был установлен.) Python будет искать в этих директориях (в заданном порядке) файл с расширением «.py», имя которого совпадает с тем, что вы пытаетесь импортировать.
Вообще-то я вас обманул; истинное положение дел немного сложнее, потому что не все модули лежат в файлах с расширением «.py». Некоторые из них, как, например, модуль sys, являются встроенными; они впаяны в сам Python. Встроенные модули ведут себя точно так же, как обычные, но их исходный код недоступен, потому что они не были написаны на Python! (Модуль sys написан на Си.)
Можно добавить новую директорию в путь поиска, добавив имя директории в список sys.path, во время выполнения Python, и тогда Python будет просматривать её наравне с остальными, как только вы попытаетесь импортировать модуль. Новый путь поиска будет действителен в течение всего сеанса работы Python.
Выполнив команду sys.path.insert(0, новый_путь), вы вставили новую директорию на первое место в список sys.path, и, следовательно, в начало пути поиска модулей. Почти всегда, именно это вам и нужно. В случае конфликта имён (например, если Python поставляется со 2-й версией некоторой библиотеки, а вы хотите использовать версию 3) этот приём гарантирует, что будут найдены и использованы ваши модули, а не те, которые идут в комплекте с Python.

Всё является объектом

Если вы вдруг пропустили, я только что сказал, что функции в Python имеют атрибуты, и эти атрибуты доступны во время выполнения. Функция, как и всё остальное в Python, является объектом.

Запустите интерактивную оболочку Python и повторяйте за мной:

>>> import humansize                               ①
>>> print(humansize.approximate_size(4096, True))  ②
4.0 KiB
>>> print(humansize.approximate_size.__doc__)      ③
Преобразует размер файла в удобочитаемую для человека форму.

    Ключевые аргументы:
    size — размер файла в байтах
    a_kilobyte_is_1024_bytes — если True (по умолчанию), используются степени 1024
                                если False, используются степени 1000

    Возвращает: текстовую строку (string)

Первая строчка импортирует программу humansize в качестве модуля — фрагмента кода, который можно использовать интерактивно или из другой Python-программы. После того, как модуль был импортирован, можно обращаться ко всем его публичным функциям, классам и атрибутам. Импорт применяется как в модулях, для доступа к функциональности других модулей, так и в интерактивной оболочке Python. Это очень важная идея, и вы ещё не раз встретите её на страницах этой книги.
Когда вы хотите использовать функцию, определённую в импортированном модуле, нужно дописать к её имени название модуля. То есть вы не можете использовать просто approximate_size, обязательно humansize.approximate_size. Если вы использовали классы в Java, то для вас это должно быть знакомо.
Вместо того, чтобы вызвать функцию (как вы, возможно, ожидали), вы запросили один из её атрибутов — __doc__.
Оператор import в Python похож на require из Perl. После import в Python, вы обращаетесь к функциям модуля как модуль.функция; после require в Perl, для обращения к функциям модуля используется имя модуль::функция.

Что такое объект?

В языке Python всё является объектом, и у любого объекта могут быть атрибуты и методы. Все функции имеют стандартный атрибут __doc__, содержащий строку документации, определённую в исходном коде функции. Модуль sys — тоже объект, имеющий (кроме прочего) атрибут под названием path. И так далее.

Но мы так и не получили ответ на главный вопрос: что такое объект? Разные языки программирования определяют «объект» по-разному. В одних считается, что все объекты должны иметь атрибуты и методы. В других, что объекты могут порождать подклассы. В Python определение ещё менее чёткое. Некоторые объекты не имеют ни атрибутов, ни методов, хотя и могли бы их иметь. Не все объекты порождают подклассы. Но всё является объектом в том смысле, что может быть присвоено переменной или передано функции в качестве аргумента.

Возможно, вы встречали термин «объект первого класса» в других книгах о программировании. В Python функции — объекты первого класса. Функцию можно передать в качестве аргумента другой функции. Модули — объекты первого класса. Весь модуль целиком можно передать в качестве аргумента функции. Классы — объекты первого класса, и отдельные их экземпляры — тоже объекты первого класса.

Это очень важно, поэтому я повторю это, на случай если вы пропустили первые несколько раз: всё в Python является объектом. Строки — это объекты. Списки — объекты. Функции — объекты. Классы — объекты. Экземпляры классов — объекты. И даже модули являются объектами.

Отступы

Функции в Python не имеют ни явных указаний begin и end, ни фигурных скобок, которые бы показывали, где код функции начинается, а где заканчивается. Разделители — только двоеточие (:) и отступы самого кода.

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(‘число слишком большое’)

Блоки кода определяются по их отступам. Под «блоками кода» я подразумеваю функции, блоки if, циклы for и while и т. д. Увеличение отступа начинает блок, а уменьшение — заканчивает. Ни скобок, ни ключевых слов. Это означает, что пробелы имеют важное значение, и их количество тоже. В этом примере код функции отбит четырьмя пробелами. Не обязательно здесь должны быть именно четыре пробела, просто их число должно быть постоянным. Первая встретившаяся строчка без отступа будет означать конец функции.
За оператором if должен следовать блок кода. Если в результате вычисления условного выражения оно окажется истинным, то выполнится блок, выделенный отступом, в противном случае произойдёт переход к блоку else (если он есть). Обратите внимание, что вокруг выражения скобки не стоят.
Эта строчка находится внутри блока if. Оператор raise вызывает исключение (типа ValueError), но только если size < 0.
Это ещё не конец функции. Совсем пустые строки не считаются. Они могут повысить читаемость кода, но не могут служить разделителями блоков кода. Блок кода функции продолжается на следующей строке.
Оператор цикла for тоже начинает блок кода. Блоки кода могут содержать несколько строк, а именно — столько, сколько строк имеют такую же величину отступа. Этот цикл for содержит три строки кода. Других синтаксических конструкций для описания многострочных блоков кода нет. Просто делайте отступы, и будет вам счастье!

После того, как вы переборете внутренние противоречия и проведёте пару ехидных аналогий с Фортраном, вы подружитесь с отступами и начнёте видеть их преимущества. Одно из главных преимуществ — то, что все программы на Python выглядят примерно одинаково, поскольку отступы — требование языка, а не вопрос стиля. Благодаря этому становится проще читать и понимать код на Python, написанный другими людьми.

В Python используются символы возврата каретки для разделения операторов, а также двоеточие и отступы для разделения блоков кода. В C++ и Java используются точки с запятой для разделения операторов и фигурные скобки для блоков кода.

Исключения

Исключения (англ. exceptions — нештатные, исключительные ситуации, требующие специальной обработки) используются повсюду в Python. Буквально каждый модуль в стандартной библиотеке Python использует их, да и сам Python вызывает их во многих ситуациях. Вы ещё неоднократно встретите их на страницах этой книги.

Что такое исключение? Обычно это ошибка, признак того, что что-то пошло не так. (Не все исключения являются ошибками, но пока это не важно.) В некоторых языках программирования принято возвращать код ошибки, который вы потом проверяете. В Python принято использовать исключения, которые вы обрабатываете.

Когда происходит ошибка в оболочке Python, она выводит кое-какие подробности об исключении и о том, как это случилось, и всё. Это называется необработанным исключением. Когда это исключение было вызвано, не нашлось никакого программного кода, чтобы заметить его и обработать должным образом, поэтому оно всплыло на самый верхний уровень — в оболочку Python, которая вывела немного отладочной информации и успокоилась. В оболочке это не так уж страшно, однако если это произойдёт во время работы настоящей программы, то вся программа с грохотом упадёт, если исключение не будет обработано. Может быть это то, что вам нужно, а может, и нет.

В отличие от Java, функции в Python не содержат объявлений о том, какие исключения они могут вызывать. Вам решать, какие из возможных исключений необходимо отлавливать.

Результат исключения — это не всегда полный крах программы. Исключения можно обработать. Иногда исключения возникают из-за настоящих ошибок в вашем коде (например, доступ к переменной, которая не существует), но порой исключение — это нечто, что вы можете предвидеть. Если вы открываете файл, он может не существовать. Если вы импортируете модуль, он может быть не установлен. Если вы подключаетесь к базе данных, она может быть недоступна или у вас может быть недостаточно прав для доступа к ней. Если вы знаете, что какая-то строка кода может вызвать исключение, то его следует обработать с помощью блока try…except.

Python использует блоки try…except для обработки исключений и оператор raise для их генерации. Java и C++ используют блоки try…catch для обработки исключений и оператор throw для их генерации.

Функция approximate_size() вызывает исключение в двух разных случаях: если переданный ей размер (size) больше, чем функция может обработать, или если он меньше нуля.

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

Синтаксис вызова исключений достаточно прост. Надо написать оператор raise, за ним название исключения и опционально, поясняющую строку для отладки. Синтаксис напоминает вызов функции. (На самом деле, исключения реализованы как классы, и оператор raise просто создаёт экземпляр класса ValueError и передаёт в его метод инициализации строку ‘число должно быть неотрицательным’. Но мы забегаем вперёд!)

Нет необходимости обрабатывать исключение в той функции, которая его вызвала. Если одна функция не обработает его, исключение передаётся в функцию, вызвавшую эту, затем в функцию, вызвавшую вызвавшую, и т. д. «вверх по стеку». Если исключение нигде не будет обработано, то программа упадёт, а Python выведет «раскрутку стека» (англ. traceback) в стандартный поток ошибок — и на этом конец. Повторяю, возможно, это именно то, что вам нужно, — это зависит от того, что делает ваша программа.

Отлов ошибок импорта

Одно из встроенных исключений Python — ImportError (ошибка импорта), которое вызывается, если не удаётся импортировать модуль. Это может случиться по нескольким причинам, самая простая из которых — отсутствие модуля в пути поиска, оператора import. Что можно использовать для включения в программу опциональных возможностей. Например, библиотека chardet предоставляет возможность автоматического определения кодировки символов. Предположим, ваша программа хочет использовать эту библиотеку в том случае, если она есть, или спокойно продолжить работу, если пользователь не установил её. Можно сделать это с помощью блока try…except.

try:
  import chardet
except ImportError:
  chardet = None

После этого можно проверять наличие модуля chardet простым if:

if chardet:
  # что-то сделать
else:
  # продолжить дальше

Другое частое применение исключения ImportError — выбор из двух модулей, предоставляющих одинаковый интерфейс (API), причём применение одного из них предпочтительнее другого (может, он быстрее работает или требует меньше памяти). Для этого можно попытаться импортировать сначала один модуль, и если это не удалось , то импортировать другой. К примеру, в главе XML рассказывается о двух модулях, реализующих один и тот же API, так называемый ElementTree API. Первый — lxml — сторонний модуль, который необходимо скачивать и устанавливать самостоятельно. Второй — xml.etree.ElementTree — медленнее, но входит в стандартную библиотеку Python 3.

try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

При выполнении этого блока try…except будет импортирован один из двух модулей под именем etree. Поскольку оба модуля реализуют один и тот же API, то в последующем коде нет необходимости проверять, какой из этих модулей был импортирован. И раз импортированный модуль в любом случае именуется как etree, то не придётся вставлять лишние if для обращения к разноимённым модулям.

Несвязанные переменные

Взглянем ещё раз на вот эту строчку функции approximate_size():

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000

Мы нигде не объявляли переменную multiple (множитель), мы только присвоили ей значение. Всё в порядке, Python позволяет так делать. Что он не позволит сделать, так это обратиться к переменной, которой не было присвоено значение. Если попытаться так сделать, возникнет исключение NameError (ошибка в имени).

>>> x
Traceback (most recent call last):
  File «<stdin>», line 1, in <module>
NameError: name ‘x’ is not defined
>>> x = 1
>>> x
1

Перевод сообщения оболочки:

Раскрутка стека (список последних вызовов):
  Файл «<stdin>», строка 1, <модуль>
NameError: имя ‘x’ не определено

Однажды вы скажете Python «спасибо» за это.

Всё чувствительно к регистру

Все имена в Python чувствительны к регистру — имена переменных, функций, классов, модулей, исключений. Всё, что можно прочитать, записать, вызвать, создать или импортировать, чувствительно к регистру.

>>> an_integer = 1
>>> an_integer
1
>>> AN_INTEGER
Traceback (most recent call last):
  File «<stdin>», line 1, in <module>
NameError: name ‘AN_INTEGER’ is not defined
>>> An_Integer
Traceback (most recent call last):
  File «<stdin>», line 1, in <module>
NameError: name ‘An_Integer’ is not defined
>>> an_inteGer
Traceback (most recent call last):
  File «<stdin>», line 1, in <module>
NameError: name ‘an_inteGer’ is not defined

Перевод сообщений оболочки:

Раскрутка стека (список последних вызовов):
  Файл «<stdin>», строка 1, <модуль>
NameError: имя ‘<имя>’ не определено

И так далее.

Запуск скриптов

В Python всё является объектом.

Модули Python — это объекты, имеющие несколько полезных атрибутов. И это обстоятельство можно использовать для простого тестирования модулей, при их написании, путём включения особого блока кода, который будет исполняться при запуске файла из командной строки. Взгляните на последние строки humansize.py:

if __name__ == ‘__main__’:
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

Как и в C, в Python используется оператор == для проверки на равенство и оператор = для присваивания. Но в отличие от C, Python не поддерживает присваивание внутри другого выражения, поэтому у вас не получится случайно присвоить значение вместо проверки на равность.

Итак, что же делает этот блок if особенным? У всех модулей, как у объектов, есть встроенный атрибут __name__ (имя). И значение этого атрибута зависит от того, как модуль используется. Если модуль импортируется, то __name__ принимает значение равное имени файла модуля, без расширения и пути к каталогу.

>>> import humansize
>>> humansize.__name__
‘humansize’

Но модуль можно запустить и напрямую, как самостоятельную программу, в этом случае __name__ примет особое значение — __main__. Python вычислит значение условного выражения в операторе if, определит его истинность, и выполнит блок кода if. В данном случае, будут напечатаны два значения.

c:\home\diveintopython3> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

И это ваша первая программа на Python!

Материалы для дальнейшего чтения

Примечания

  1. ↑ В английском языке апострофы, обрамляющие текст, — это уже одинарные кавычки. — Прим. пер.
  2. ↑ Имеется ввиду тип данных string (строка). — Прим. пер.

Подборка материалов для успешного изучения языка Python

Python, один из распространенных языков программирования, приобретает все большую популярность. Возможно, со временем он потеснит PHP с его позиций. Если вы решили изучить этот язык, данная подборка книг и видеоматериалов поможет вам сделать это максимально эффективно.

Официальная документация

Незаменимый источник информации не только для различных программ, но и для освоения языков. Все сведения, которые вам могут понадобиться, находятся именно здесь.

Книги по Python

«Изучение Python 3. Сложный путь», автор – Зед Шоу

Многие считают книгу «Learn Python 3 the Hard Way» лучшим самоучителем по этому языку программирования. Несмотря на то, что официальная документация охватывает все подробности, книга, написанная простым человеческим языком, более “читабельна” и интересна. По словам автора, книга позволит научиться кодить даже совершенным новичкам, освоение материала не требует какой-то особой начальной подготовки. Приложенные видеоматериалы в HD-качестве обеспечат наглядность. Книга на английском языке.

«Погружение в Python 3», автор – Марк Пилгрим

Работу Марка Пилгрима давно оценили программисты всего мира. Книга «Dive Into Python 3» охватывает сведения о третьей версии языка и ее отличиях от второй. Это переработанное издание первоначального варианта «Погружение в Python» («Dive Into Python»), обновленное на 80%. Весомым преимуществом этих книг является их бесплатность: обе выложены на сайтах для свободного доступа. Первое издание есть и на русском языке.

«Изучите Python за 10 минут», автор – Ставрос Корокитакис.

Очень любопытная статья на английском. Ориентирована на тех, кому нужно разобраться в языке по-быстрому, однако извлечь для себя что-то полезное могут все. Автор раскрывает основы и приводит примеры, что, по идее, позволит вам после прочтения и усвоения писать несложные коды самостоятельно.

Видео

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

Для тех, кто вообще не сталкивался прежде с языками программирования, есть бесплатный вводный курс «Программирование на Python». В рамках курса доходчиво объясняются базовые понятия и элементы, есть домашние задания, которые проверяет автоматическая система. Также можно обсудить возникающие вопросы в комментариях к задачам.

Практика

Чтобы хорошо что-либо изучить, нужно много тренироваться. Для изучения языка программирования пригодятся специально составленные задачи, которые вы можете найти на этих сайтах.

Отдельно стоит упомянуть pythonchallenge.com, где сложность задач растет по мере вашего продвижения.

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

Классы и итераторы — Погружение в Python 3

Погружение

Генераторы на самом деле — всего лишь частный случай итераторов. Функция, возвращающая (yields) значения, является простым и компактным способом получения функциональности итератора, без непосредственного создания итератора. Помните генератор чисел Фибоначчи? Вот набросок того, как мог бы выглядеть аналогичный итератор:

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? Что такое класс?

Определение классов

Python полностью объектно-ориентирован, то есть вы можете определять свои собственные классы, наследовать новые классы от своих или встроенных классов, и создавать экземпляры классов, которые уже определили.

Определить класс в Python просто. Также как и в случае с функциями, раздельное объявление интерфейса не требуется. Вы просто определяете класс и начинаете программировать. Определение класса в Python начинается с зарезервированного слова class, за которым следует имя (идентификатор) класса. Формально, это все, что необходимо, в случае, когда класс не должен быть унаследован от другого класса.

  1. ↑ Определенный выше класс имеет имя PapayaWhip и не наследует никакой другой класс. Имена классов, как правило, пишутся с большой буквы, НапримерВотТак, но это всего лишь соглашение, а не требование.
  2. ↑ Вы наверное уже догадались, что каждая строка в определении класса имеет отступ, также как и в случае с функциями, оператором условного перехода if, циклом for или любым другим блоком кода. Первая строка без отступа находится вне блока class.

Класс PapayaWhip не содержит определений методов или атрибутов, но с точки зрения синтаксиса, тело класса не может оставаться пустым. В таких случаях используется оператор pass. В языке Python pass — зарезервированное слово, которое говорит интерпретатору: «идем дальше, здесь ничего нет». Это оператор не делающий ровным счетом ничего, но тем не менее являющийся удобным решением, когда вам нужно сделать заглушку для функции или класса.

Выражение pass в языке Python аналог пустого множества или фигурных скобок в языках Java или C++.

Многие классы наследуются от других классов, но не этот. Многие классы определяют свои методы, но не этот. Класс в Python не обязан иметь ничего, кроме имени. В частности, людям знакомым с C++ может показаться странным, что у класса в Python отсутствуют в явном виде конструктор и деструктор. Несмотря на то, что это не является обязательным, класс в Python может иметь нечто, похожее на конструктор: метод __init__().

Метод __init__()

В следующем примере демонстрируется инициализация класса Fib, с помощью метода __init__().

class Fib:
    »’iterator that yields numbers in the Fibonacci sequence»’

[K 1]

    def __init__(self, max):

[K 2]
  1. ↑ Классы, по аналогии с модулями и функциями могут (и должны) иметь строки документации (docstrings).
  2. ↑ Метод __init__() вызывается сразу же после создания экземпляра класса. Было бы заманчиво, но формально неверно, считать его «конструктором» класса. Заманчиво, потому что он напоминает конструктор класса в языке C++: внешне (общепринято, что метод __init__() должен быть первым методом, определенным для класса), и в действии (это первый блок кода, исполняемый в контексте только что созданного экземпляра класса). Неверно, потому что на момент вызова __init__() объект уже фактически является созданным, и вы можете оперировать корректной ссылкой на него (self)

Первым аргументов любого метода класса, включая метод __init__(), всегда является ссылка на текущий экземпляр класса. Принято называть этот аргумент self. Этот аргумент выполняет роль зарезервированного слова this в C++ или Java, но, тем не менее, в Python self не является зарезервированным. Несмотря на то, что это всего лишь соглашение, пожалуйста не называйте этот аргумент как либо еще.

В случае метода __init__(), self ссылается на только что созданный объект; в остальных методах — на экземпляр, метод которого был вызван. И, хотя вам необходимо явно указывать self при определении метода, при вызове этого не требуется; Python добавит его для вас автоматически.

Создание экземпляров

Для создания нового экземпляра класса в Python нужно вызвать класс, как если бы он был функцией, передав необходимые аргументы для метода __init__(). В качестве возвращаемого значения мы получим только что созданный объект.

>>> import fibonacci2
>>> fib = fibonacci2.Fib(100)

[K 1]
[K 2]

<fibonacci2.Fib object at 0x00DB8810>
>>> fib.__class__

[K 3]

<class ‘fibonacci2.Fib’>
>>> fib.__doc__

[K 4]

‘iterator that yields numbers in the Fibonacci sequence’

  1. ↑ Вы создаете новый экземпляр класса Fib (определенный в модуле fibonacci2) и присваиваете только что созданный объект переменной fib. Единственный переданный аргумент, 100, соответствует именованному аргументу max, в методе __init__() класса Fib.
  2. ↑ fib теперь является экземпляром класса Fib
  3. ↑ Каждый экземпляр класса имеет встроенный атрибут __class__, который указывает на класс объекта. Java программисты могут быть знакомы с классом Class, который содержит методы getName() и getSuperclass(), используемые для получения информации об объекте. В Python, метаданные такого рода доступны через соответствующие атрибуты, но используемая идея та же самая.
  4. ↑ Вы можете получить строку документации (docstring) класса, по аналогии с функцией и модулем. Все экземпляры класса имеют одну и ту же строку документации.

Для создания нового экземпляра класса в Python, просто вызовите класс, как если бы он был функцией, явные операторы, как например new в С++ или Java, в языке Python отсутствуют.

Переменные экземпляра

Перейдем к следующей строчке:

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

[K 1]
  1. ↑ Что такое self.max? Это переменная экземпляра. Она не имеет ничего общего с переменной max, которую мы передали в метод __init__() в качестве аргумента. self.max является «глобальной» для всего экземпляра. Это значит, что вы можете обратиться к ней из других методов.

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

[K 1]

.
.
.
    def __next__(self):
        fib = self.a
        if fib > self.max:

[K 2]
  1. ↑ self.max определена в методе __init__()…
  2. ↑ …и использована в методе __next__().

Переменные экземпляра связаны только с одним экземпляром класса. Например, если вы создадите два экземпляра класса Fib с разными максимальными значениями, каждый из них будет помнить только свое собственное значение.

>>> import fibonacci2
>>> fib1 = fibonacci2.Fib(100)
>>> fib2 = fibonacci2.Fib(200)
>>> fib1.max
100
>>> fib2.max
200

Итератор чисел Фибоначчи

Теперь вы готовы узнать как создать итератор. Итератор это обычный класс, который определяет метод  __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 должна быть классом, а не функцией.

Замыкания и генераторы — Погружение в Python 3

Погружение

По причинам, превосходящим всяческое понимание, я всегда восхищался языками. Не языками программирования. Хотя да, ими, а также языками естественными. Возьмем, к примеру, английский. Английский язык — это шизофренический язык, который заимствует слова из немецкого, французского, испанского и латинского языков (не говоря уже об остальных). Откровенно говоря, «заимствует» — неуместное слово; он их скорее «ворует». Или, возможно, «ассимилирует» — как Борги. Да, хороший вариант.

«Мы Борги. Ваши лингвистические и этимологические особенности станут нашими. Сопротивление бесполезно.»

В этой главе вы узнаете о существительных во множественном числе. Также вы узнаете о функциях, которые возвращают другие функции, о сложных регулярных выражениях и генераторах. Но сначала давайте поговорим о том, как образуются существительные во множественном числе. (Если вы не читали раздел посвященный регулярным выражениям, сейчас — самое время. Материал этого раздела подразумевает, что вы понимаете основы регулярных выражений, и довольно быстро перейдете к их нетривиальному использованию).

Если вы выросли в англоязычной стране или изучали английский в формальной школьной обстановке, вы, вероятно, знакомы с основными правилами:

  1. Если слово заканчивается буквами S, X или Z, следует добавить ES. Bass становится basses, fax становится faxes, а waltz — waltzes.
  2. Если слово заканчивается звонкой H, следует добавить ES; если заканчивается глухой H, то нужно просто добавить S. Что такое звонкая H? Это такая, которая вместе с другими буквами объединяется в звук, который вы можете расслышать. Соответственно, coach становится coaches, и rash становится rashes, потому что вы слышите звуки CH и SH, когда произносите эти слова. Но cheetah становится cheetahs, потому что H здесь глухая.
  3. Если слово заканчивается на Y, которая читается как I, то замените Y на IES; если Y объединена с гласной и звучит как-то по-другому, то просто добавьте S. Так что vacancy становится vacancies, но day становится days.
  4. Если ни одно из правил не подходит, просто добавьте S и надейтесь на лучшее.

(Я знаю, существует множество исключений. Man становится men, а woman — women, но human становится humans. Mouse — mice, а louse — lice, но house во множественной числе — houses. Knife становится knives, а wife становится wives, но lowlife становится lowlifes. И не заставляйте меня вглядываться в слова, которые не изменяются во множественном числе, как например sheep, deer или haiku).

Остальные языки, конечно, совершенно другие.

Давайте разработаем библиотеку на Python, которая автоматически образует множественное число английского слова. Мы начнем с этих четырех правил, но не забывайте, что вам неизбежно понадобится добавлять еще.

Я знаю, используем регулярные выражения!

Итак, вы смотрите на слова, и, по крайней мере в английском, это означает, что вы смотрите на последовательности символов. У вас есть правила, которые говорят, что нужно искать разные комбинации символов, потом совершать с ними различные действия. Похоже, это работа для регулярных выражений!

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’

  1. ↑ Это регулярное выражение, но оно использует синтаксис, который вы не видели в главе Регулярные Выражения. Квадратные скобки означают «найти совпадения ровно с одним из этих символов». Поэтому [sxz] означает «s или x, или z», но только один из них. Символ $ должен быть знаком вам. Он ищет совпадения с концом строки. Все регулярное выражение проверяет, заканчивается ли noun на s, x или z.
  2. ↑ Упомянутая функция re.sub() производит замену подстроки на основе регулярного выражения.

Рассмотрим замену с помощью регулярного выражения внимательнее.

>>> 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 символы a, b или c? Да, содержит a.
  2. Отлично, теперь ищем a, b или c и заменяем на o. Mark становится Mork.
  3. Та же функция превращает rock в rook.
  4. Вы могли подумать, что этот код caps преобразует в oaps, но он этого не делает. re.sub заменяет все совпадения, а не только первое найденное. Так что, данное регулярное выражение превратит caps в oops, потому что оба символа c и a заменяются на o.

Вернемся снова к функции 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. Здесь вы заменяете конец строки (найденный с помощью символа $) на строку es. Другими словами, добавляете es к строке. Вы могли бы совершить то же самое с помощью конкатенации строк, например, как noun + ‘es’, но я предпочел использовать регулярные выражения для каждого правила, по причинам которые станут ясны позже.
  2. Взгляните-ка, это регулярное выражение содержит кое-что новое. Символ ^ в качестве первого символа в квадратных скобках имеет особый смысл: отрицание. [^abc] означает «любой отдельный символ кроме a, b или c». Так что [^aeioudgkprt] означает любой символ кроме a, e, i, o, u, d, g, k, p, r или t. Затем за этим символом должен быть символ h, следом за ним — конец строки. Вы ищете слова, заканчивающиеся на H, которую можно услышать.
  3. То же самое здесь: найти слова, которые заканчиваются на Y, в которых символ перед Y — не a, e, i, o или u. Вы ищете слова, заканчивающиеся на Y, которая звучит как I.

Давайте внимательнее рассмотрим регулярные выражения с участием отрицания.

>>> 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 не подходит, потому что оно заканчивается на oy, а вы конкретно указали, что символ перед y не может быть o. day не подходит, потому что он заканчивается на 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, что вам и нужно. Заметьте, что оно бы преобразовало boy в boies, но этого в функции никогда не произойдет, потому что вы сначала сделали re.search с целью выяснить, следует ли делать re.sub.
  2. Замечу заодно, что возможно объединить эти два регулярных выражения (одно чтобы выяснить применяется ли правило, а другое чтобы собственно его применить) в одно регулярное выражение. Вот так выглядел бы результат. Большая часть должна быть вам знакома: вы используете запоминаемую группу, о которой вы узнали из Учебный пример: Разбор телефонного номера. Группа используется чтобы запомнить символ перед y. Затем в подстановочной строке, вы используете новый синтаксис, \1, который означает «эй, та первая группа, которую ты запомнил? положи ее сюда». Таким образом, вы помните c перед y; когда вы делаете подстановку, вы ставите c на место c, и ies на место y. (Если у вас более одной запоминаемой группы, можете использовать \2 и \3 и так далее.)

Замены с использованием регулярных выражений являются чрезвычайно мощным инструментом, а синтаксис \1 делает их еще более мощным. Но вся операция, объединенная в одно регулярное выражение, также становится сложной для чтения, кроме того такой способ не соотносится напрямую с тем, как вы изначально описали правила формирования множественного числа. Изначально вы спроектировали правила в форме «если слово заканчивается на S, X или Z, то добавьте ES». А если вы смотрите на функцию, то у вас — две строки кода, которые говорят «если слово заканчивается на S, X или Z, то добавьте ES». Еще ближе к оригинальному варианту приблизиться никак не получится.

Список функций

Сейчас вы добавите уровень абстракции. Вы начали с определения списка правил: если верно это, сделай то, иначе обращайтесь к следующему правилу. Давайте временно усложним часть программы, так что вы сможете упростить другую ее часть.

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)
       
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. Теперь каждое правило-условие совпадения является отдельной функцией которая возвращает результаты вызова функции re.search().
  2. Каждое правило-действие также является отдельной функцией, которая вызывает функцию re.sub() чтобы применить соответствующее правило формирования множественного числа.
  3. Вместо одной функции (plural()) с несколькими правилами у вас теперь есть структура данных rules, являющаяся последовательностью пар функций.
  4. Поскольку правила развернуты в отдельной структуре данных, новая функция plural() может быть сокращена до нескольких строк кода. Используя цикл for, из структуры rules можно извлечь правила условия и замены одновременно. При первой итерации for цикла, match_rules станет match_sxz, а apply_rule станет apply_sxz. Во время второй итерации, если мы до нее дойдем, matches_rule будет присвоено match_h, а apply_rule станет apply_h. Функция гарантированно вернет что-нибудь по окончании работы, потому что последнее правило совпадения (match_default) просто возвращает True, подразумевая, что соответствующее правило замены (apply_default) всегда будет применено.

Причиной, по которой этот пример работает, является тот факт, что в Python все является объектом, даже функции. Структура данных rules содержит функции — не имена функций, а фактические функции-объекты. Когда они присваиваются в for цикле, matches_rule и apply_rule являются настоящими функциями, которые вы можете вызывать. При первой итерации for цикла, это эквивалентно вызову matches_sxz(noun), и если она возвращает совпадение, вызову apply_sxz(noun).

-> Переменная «rules» — это последовательность пар функций.

Если этот дополнительный уровень абстракции сбивает вас с толку, попробуйте развернуть функцию, чтобы увидеть, что мы получаем то же самое. Весь цикл for эквивалентен следующему:

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)

Преимуществом здесь является то, что функция plural() упрощена. Она принимает последовательность правил, определенных где-либо, и проходит по ним.

  1. Получить правило совпадения
  2. Правило срабатывает? Тогда применить правило замены и вернуть результат.
  3. Нет совпадений? Начать с пункта 1.

Правила могут быть определены где угодно, любым способом. Для функции plural() абсолютно нет никакой разницы.

Итак, добавление этого уровня абстракции стоило того? Вообще-то пока нет. Попробуем представить, что потребуется для добавления нового правила в функцию. В первом примере этого потребовало бы добавить новую конструкцию if в функцию plural(). Во втором примере, это потребовало бы добавить две функции, match_foo() и apply_foo(), а затем обновить последовательность rules чтобы указать, когда новые правила совпадения и замены должны быть вызваны по отношению к остальным правилам.

Но на самом деле это только средство, чтобы перейти к следующей главе. Двигаемся дальше…

Список шаблонов

Определение отдельных именованных функций для каждого условия и правила замены вовсе не является необходимостью. Вы никогда не вызываете их напрямую; вы добавляете их в последовательность rules и вызываете их через нее. Более того, каждая функция следует одному из двух шаблонов. Все функции совпадения вызывают re.search(), а все функции замены вызывают re.sub(). Давайте исключим шаблоны, чтобы объявление новых правил было более простым.

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(), которая вызывает re.search() с шаблоном pattern, переданный функции build_match_and_apply_functions() в качестве аргумента, и word, который передан функции matches_rule(), которую вы определяете.
  2. Строим функцию apply тем же способом. Функция apply — это функция, которая принимает один параметр, и вызывает re.sub() с search и replace параметрами, переданными функции build_match_and_apply_functions, и word, переданным функции apply_rule(), которую вы создаете. Подход, заключающийся в использовании значений внешних параметров внутри динамической функции называется замыканиями. По сути вы определяете константы в функции замены: он принимает один параметр (word), но затем действует используя его и два других значения (search и replace), которые были установлены в момент определения функции замены.
  3. В конце концов функция build_match_and_apply_functions() возвращает кортеж с двумя значениями, двумя функциями, которые вы только что создали. Константы, которые вы определили внутри тех функций (pattern внутри функции match_rule()), search и replace в функции apply_rule()) остаются с этими функциями, даже. Это безумно круто.

Если это сбивает вас с толку (и так и должно быть, это весьма странное поведение), картина может проясниться, когда вы увидите как использовать этот подход.

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. Наши правила формирования множественного числа теперь определены как кортеж кортежей строк (не функций). Первая строка в каждой группе — это регулярное выражение, которое вы бы использовали в re.search() чтобы определить, подходит ли данное правило. Вторая и третья строки в каждой группе — это выражения для поиска и замены, которые вы бы использовали в re.sub() чтобы применить правило и преобразовать существительное во множественное число.
  2. В альтернативном правиле есть небольшое изменение. В прошлом примере функция match_default() просто возвращает True, подразумевая, что если ни одно конкретное правило не применилось, код должен просто добавить s в конец данного слова. Функционально данный пример делает то же самое. Окончательное регулярное выражение узнает, заканчивается ли слово ($ ищет конец строки). Конечно же, у каждой строки есть конец, даже у пустой, так что выражение всегда срабатывает. Таким образом, она служит той же цели, что и функция match_default(), которая всегда возвращала True: она гарантирует, что если нет других конкретных выполненных правил, код добавляет s в конец данного слова.
  3. Это волшебная строка. Она принимает последовательность строк в patterns и превращает их в последовательность функций. Как? «Отображением» строк в функцию build_and_apply_functions(). То есть она берет каждую тройку строк и вызывает функцию build_match_and_apply_functions() с этими тремя строками в качестве аргументов. Функция build_match_and_apply_functions() возвращает кортеж из двух функций. Это означает, что rules в конце концов функционально становится эквивалентной предыдущему примеру: список кортежей, где каждый кортеж — это пара функций. Первая функция — это функция совпадения, которая вызывает re.search(), а вторая функция — применение правила (замена), которая вызывает re.sub().

Завершим эту версию скрипта главной точкой входа, функцией plural().

def plural(noun):
    for matches_rule, apply_rule in rules:  ①
        if matches_rule(noun):
            return apply_rule(noun)

  1. Поскольку список rules — тот же самый, что и в предыдущем примере (да, так и есть), нет ничего удивительного в том, что функция plural() совсем не изменилась. Она является полностью обобщенной; она принимает список функций-правил и вызывает их по порядку. Ее не волнует, как определены правила. В предыдущем примере они были определены как отдельные именованные функции. Теперь же они создаются динамически сопоставлением результата функции build_match_and_apply_functions() списку обычных строк. Это не играет никакой роли. функция plural() продолжает работать как и раньше.

Файл шаблонов

Вы вынесли весь дублирующийся код и добавили достаточно абстракций для возможности хранить правила формирования множественного числа в списке строк. Следующий логический этап — взять эти строки и расположить их в отдельном файле, где они могут поддерживаться отдельно от использующего их кода.

Во-первых, давайте создадим текстовый файл, содержащий нужные нам правила. Никаких сложных структур данных, просто разделенные на три колонки данные. Назовем его plural4-rules.txt

[sxz]$               $    es
[^aeioudgkprt]h$     $    es
[^aeiou]y$          y$    ies
$                    $    s

Теперь давайте посмотрим, как вы можете использовать этот файл с правилами.

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))

  1. Функция build_match_and_apply_functions() не изменилась. Вы по-прежнему используете замыкания, чтобы динамически создать две функции, которые будут использовать переменные из внешней функции.
  2. Глобальная функция open() открывает файл и возвращает файловый объект. В данном случае файл, который мы открываем, содержит строки-шаблоны для правил формирования множественного числа. Утверждение with создает то, что называется контекстом: когда блок with заканчивается, Python автоматически закроет файл, даже если внутри блока with было выброшено исключение. Подробнее о блоках with и файловых объектах вы узнаете из главы Файлы.
  3. Форма «for line in <fileobject>» читает данные из открытого файла построчно и присваивает текст переменной line. Подробнее про чтение файлов вы узнаете из главы Файлы.
  4. Каждая строка в файле действительно содержит три значения, но они разделены пустым пространством (табуляцией или пробелами, без разницы). Чтобы разделить их, используйте строковый метод split(). Первый аргумент для split() — None, что означает «разделить любым символом свободного пространства (табуляцией или пробелом, без разницы)». Второй аргумент — 3, что означает «разбить свободным пространством 3 раза, затем оставить остальную часть строки» Строка вида «[sxz]$ $ es» будет разбита и преобразована в список [‘[sxz]$’, ‘$’, ‘es’], что означает что pattern станет ‘[sxz]$’, search — ‘$’, а replace получит значение ‘es’. Это довольно мощно для одной маленькой строки кода
  5. В конце концов, вы передаете pattern, search и replace функции build_match_and_apply_function(), которая возвращает кортеж функций. Вы добавляете этот кортеж в список rules, и в завершении rules хранит список функций поиска совпадений и выполнения замен, который ожидает функция plural().

Сделанное улучшение заключается в том, что вы полностью вынесли правила во внешний файл, так что он может поддерживаться отдельно от использующего его кода. Код — это код, данные — это данные, а жизнь хороша.

Генераторы

Но ведь будет круто если обобщенная функция plural() будет разбирать файл с правилами? Извлеки правила, найди совпадения, примени соответствующие изменения, переходи к следующему правилу. Это все, что функции 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))

Как черт возьми это работает? Давайте сначала посмотрим на пример с пояснениями.

>>> def make_counter(x):
…     print(‘entering make_counter’)
…     while True:
…         yield x ①
…         print(‘incrementing x’)
…         x = x + 1

>>> counter = make_counter(2) ②
>>> counter ③
<generator object at 0x001C9C10>
>>> next(counter) ④
entering make_counter
2
>>> next(counter) ⑤
incrementing x
3
>>> next(counter) ⑥
incrementing x
4

  1. Присутствие ключевого слова yield в make_counter означает, что это не обычная функция. Это особый вид функции, которая генерирует значения по одному. Вы можете думать о ней как о продолжаемой функции. Её вызов вернёт генератор, который может быть использован для генерации последующих значений x.
  2. Чтобы создать экземпляр генератора make_counter, просто вызовите его как и любую другую функцию. Заметьте, что фактически это не выполняет кода функции. Вы можете так сказать, потому что первая строка функции make_counter() вызывает print(), но ничего до сих пор не напечатано.
  3. Функция make_counter() возвращает объект-генератор.
  4. Функция next() принимает генератор и возвращает его следующее значение. Первый раз, когда вы вызываете next() с генератором counter, он исполняет код в make_counter() до первого утверждения yield, затем возвращает значение, которое было возвращено yield. В данном случае, это будет 2, поскольку изначально вы создали генератор вызовом make_counter(2).
  5. Повторный вызов next() с тем же генератором продолжает вычисления точно там, где они были прерваны, и продолжает до тех пор, пока не встретит следующий yield. Все переменные, локальные состояния и т. д. сохраняются во время yield, и восстанавливаются при вызове next(). Следующая строка кода, ожидающая исполнения, вызывает print(), который печатает incrementing x. После этого следует утверждение x = x + 1. Затем снова исполняется цикл while, и первое, что в нём встречается — утверждение yield x, которое сохраняет все состояние и возвращает текущее значение x (сейчас это 3).
  6. После второго вызова next(counter) происходит всё то же самое, только теперь x становится равным 4.

Поскольку make_counter входит в бесконечный цикл, вы бы теоретически могли заниматься этим бесконечно, а он продолжал бы увеличивать x и возвращать его значения. Но вместо этого давайте посмотрим на более продуктивное использование генераторов.

Генератор последовательности Фибоначчи

def fib(max):
    a, b = 0, 1          ①
    while a < max:
        yield a          ②
        a, b = b, a + b  ③

  1. Последовательность Фибоначчи — это последовательность чисел, в которой каждое число является суммой двух предыдущих. Она начинается с 0 и 1, сначала постепенно возрастает, потом растет все быстрее и быстрее. Чтобы начать последовательность, вам необходимо две переменные: а начинает с 0, а b — с 1.
  2. a является начальный значением последовательности, поэтому её следует вернуть.
  3. b является следующим числом последовательности, так что присвойте ее a, но так же посчитайте следующее значение (a + b) и присвойте его b для последующего использования. Заметьте, что это происходит одновременно. Если a равно 3, а b равно 5, то a, b = b, a + b установит a в 5 (предыдущее значение b) а b в 8 (сумма предыдущих значений a и b).

Так что теперь у вас есть функция выдает последовательные числа Фибоначчи. Конечно, вы могли бы сделать это с помощью рекурсии, но данная реализация проще читается. Помимо этого, она лучше работает с циклами for.

->yield приостанавливает функцию, next() снова запускает ее в том же состоянии

>>> 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. Вы можете использовать генератор типа fib() непосредственно в for цикле. Цикл for автоматически вызоывает функцию next() чтобы получить значения из генератора fib() и присвоить их переменной for цикла n.
  2. Каждый раз проходя for цикл, n принимает новое значение от yield в функции fib(), и все что вам нужно сделать — напечатать его. Как только fib() выходит за пределы чисел (a становится больше, чем max, которое в данном случае равно 1000), так сразу цикл for заканчивает работу.
  3. Это полезная техника: отдайте генератор функции list(), и она пройдет в цикле весь генератор (точно так же, как и цикл for в предыдущем примере) и вернет список всех значений.

A Plural Rule Generator

Давайте вернемся к plural5.py и посмотрим как работает эта версия функции 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. Никакой магии. Помните, что строки файла правил содержат по три значения, разделенных пустым пространством, так что вы используете line.split(None, 3) чтобы получить три «колонки» и присвоить их трем локальным переменным.
  2. А затем вы вызываете yield. Что вы возвращаете? Две функции, созданные динамически вашим старым помощником, build_match_and_apply_functions(), который такой же как и в предыдущих примерах. Другими словами, rules() — это генератор, который отдаёт правила совпадения и изменения по требованию.
  3. Поскольку rules() — это генератор, вы можете использовать его напрямую в for цикле. При первом прохождении for цикла, вы вызовете функцию rules(), которая откроет файл шаблонов, прочитает первую строку, динамически построит функцию условия и модификации из шаблона в этой строке, и вернет динамически созданные функции. При прохождении через loop цикл второй раз, вы продолжите ровно с того места, в котором покинули rules() (это было внутри for line in pattern_file цикла). Первое, что он сделает — прочитает следующую строку файла (который до сих пор открыт), динамически создаст другие функции условия и модификации на основании шаблонов этой строки файла, и отдаст эти две функции.

Что вы приобрели по сравнению с вариантом 4? Меньшее время запуска. В варианте 4 когда вы импортировали модуль plural4, он читал весь файл шаблонов и строил список всех возможных правил ещё до того, как вы вообще подумали о вызове функции plural(). С генераторами вы можете выполнять все действия лениво: вы читаете первое правило и создаете функции и пробуете их, и если это срабатывает вы никогда не читаете остальной файл и не создаете другие функции.

В чем вы теряете? В производительности! Каждый раз когда вы вызываете функцию plural(), генератор rules() начинает всё с начала — что означает открытие заново файла шаблонов и чтение с самого начала построчно.

Что если бы вы могли взять лучшее из двух миров: минимальные расходы на запуск (не исполнять никакого кода во время import) и максимальная производительность (не создавать одни и те же функции снова и снова). И да, вы по-прежнему хотите хранить правила в отдельном файле (потому что код — это код, а данные — это данные), настолько долго, пока вам вдруг не потребуется читать одну и ту же строку дважды.

Чтобы сделать это, вам необходимо будет построить свой собственный итератор. Но перед тем как вы сделаете это, вам необходимо изучить классы в Python.

Материалы для дальнейшего чтения

Генераторы — Погружение в Python 3

Нам приходится сильнее напрягать свое воображение не для того, чтобы, как в художественной литературе, представить себе то, чего нет на самом деле, а для того, чтобы постичь то, что действительно происходит.

Ричард Фейнман

Погружение

В каждом языке программирования есть одна такая особенность, сложно устроенная, но специально упрощённая штука. Если вы раньше писали на другом языке, можете и не обратить на это внимания, поскольку ваш старый язык не так сильно упрощал эту штуку (потому что он был занят тем, что сильно упрощал какую-нибудь другую штуку). В этой главе вы изучите генераторы списков, словарей и множеств — три взаимосвязанные концепции, сконцентрированные вокруг одной очень мощной технологии. Но сначала я хочу немного отклониться от нашего повествования, чтобы рассказать вам о двух модулях, которые помогут вам передвигаться по вашей локальной файловой системе.

Работа с файлами и каталогами

Python 3 поставляется с модулем os, что означает «операционная система». Модуль os содержит множество функций для получения информации о локальных каталогах, файлах, процессах и переменных окружения (а в некоторых случаях, и для манипулирования ими). Python предлагает очень хороший унифицированный программный интерфейс для всех поддерживаемых операционных систем, так что ваши программы можно запускать на любом компьютере с минимальным количеством платформо-зависимого кода.

Текущий рабочий каталог

Когда ваше знакомство с Python только начинается, вы много времени проводите в интерактивной оболочке Python. На протяжении всей этой книги вы будете видеть примеры, выглядящие следующим образом:

  1. Импортирование какого-либо модуля из папки примеров
  2. Вызов функции из этого модуля
  3. Объяснение результата

Всегда есть текущий рабочий каталог.

Если вы ничего не знаете о текущем рабочем каталоге, то, возможно, шаг 1 окажется неудачным и будет порождено исключение типа ImportError. Почему? Потому что Python будет искать указанный модуль в пути поиска оператора import, но не найдёт его, потому что каталог examples не содержится в путях поиска. Чтобы исправить это, вы можете сделать одно из двух:

  • либо добавить папку examples в путь поиска оператора import;
  • либо сделать текущим рабочим каталогом папку examples.

Текущий рабочий каталог является неявным параметром, который Python постоянно хранит в памяти. Текущий рабочий каталог есть всегда, когда вы работаете в интерактивной оболочке Python, запускаете свой сценарии из командной строки или CGI-сценарий где-то на веб-сервере.

Модуль 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 поставляется вместе с Python; вы можете импортировать его когда угодно и где угодно.
  2. ↑ Используйте функцию os.getcwd() для получения значения текущего рабочего каталога. Когда вы работаете в графической оболочке Python, текущим рабочим каталогом является каталог из которого она была запущена. В Windows это зависит от того, куда вы установили Python; каталог по умолчанию c:\Python31. Если оболочка Python запущена из командной строки, текущим рабочим каталогом считается тот, в котором вы находились, когда запускали её.
  3. ↑ Используйте функцию os.chdir() чтобы сменить текущий рабочий каталог.
  4. ↑ Когда я вызывал функцию os.chdir(), я использовал путь в стиле Linux (косая черта, нет буквы диска) даже если на самом деле работал в Windows. Это одно из тех мест, где Python пытается стирать различия между операционными системами.

Работа с именами файлов и каталогов

Раз зашла речь о каталогах, я хочу обратить ваше внимание на модуль os.path. Он содержит функции для работы с именами файлов и каталогов.

>>> 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() составляет путь к каталогу из одного или нескольких частичных путей. В данном случае она просто соединяет строки.
  2. ↑ Это уже менее тривиальный случай. Функция join добавит дополнительную косую черту (slash) к имени папки перед тем как дописать имя файла. В данном случае Python добавляет обратную косую черту (backslash) вместо обыкновенной, потому что я запустил этот пример в Windows. Если вы введёте данную команду в Linux или Mac OS X, вы увидите простую косую черту. Python может обратиться к файлу независимо от того, какой разделитель используется в пути к файлу.
  3. ↑ Функция os.path.expanduser() раскрывает путь, в котором используется символ ~ для обозначения домашнего каталога текущего пользователя. Функция работает на любой платформе, где у пользователя есть домашний каталог, включая Linux, Mac OS X и Windows. Функция возвращает путь без косой черты в конце, но для функции os.path.join() это не имеет значения.
  4. ↑ Комбинируя эти две функции, вы можете легко строить файловые пути для папок и файлов в домашнем каталоге пользователя. Функция os.path.join() принимает любое количество аргументов. Я получил огромное удовольствие, когда обнаружил это, так как в других языках при разработке инструментальных средств мне приходилось постоянно писать глупую маленькую функцию addSlashIfNecessary(). В языке программирования Python умные люди уже позаботились об этом.

Модуль os.path также содержит функции для разбиения файловых путей, имён папок и файлов на их составные части.

>>> 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 дробит полный путь и возвращает кортеж, содержащий отдельно путь до каталога и имя файла.
  2. ↑ Помните, я рассказывал про то, как присваивать несколько значений за раз и как вернуть одновременно несколько значений из функции? Функция os.path.split() действует именно так. Можно присвоить возвращаемое из функции split значение кортежу из двух переменных. Каждая из переменных примет значение соответствующего элемента результирующего кортежа.
  3. ↑ Первая переменная — dirname — получит значение первого элемента кортежа, возвращаемого функцией os.path.split(), а именно путь до каталога.
  4. ↑ Вторая переменная — filename — примет значение второго элемента кортежа, возвращаемого функцией os.path.split(), а именно имя файла.
  5. ↑ Модуль os.path также содержит функцию os.path.splitext(), которая дробит имя файла и возвращает кортеж, содержащий отдельно имя и отдельно расширение файла. Можно использовать ту же технику, что и ранее для присваивания каждого из интересующих значений отдельным переменным.

Получение содержимого каталога

Модуль glob понимает символы-джокеры, использующиеся в командных оболочках.

Модуль glob — это ещё один инструмент из стандартной библиотеки Python. Это простой способ программно получить содержимое папки, а также он умеет использовать символы-джокеры, с которыми вы наверняка знакомы, если работали в командной строке.

>>> os.chdir(‘/Users/pilgrim/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 принимает шаблон, содержащий символы-джокеры, и возвращает пути всех файлов и каталогов, соответствующих ему. В этом примере шаблон содержит путь к каталогу и "*.xml", которому будут соответствовать все xml-файлы в каталоге examples.
  2. ↑ Теперь сделаем текущим рабочим каталог examples. Функция os.chdir() может принимать и относительные пути.
  3. ↑ Вы можете использовать несколько символов-джокеров в своём шаблоне. Этот пример находит все файлы в текущем рабочем каталоге, заканчивающиеся на .py и содержащие слово test где-нибудь в имени файла.

Получение сведений о файле

Любая современная операционная система хранит сведения о каждом файле (метаданные): дата создания, дата последней модификации, размер файла и т. д. Python предоставляет единый программный интерфейс для доступа к этим метаданным. Вам не надо открывать файл; всё, что требуется — имя файла.

>>> print(os.getcwd())
c:\Users\pilgrim\diveintopython3\examples

[1]

>>> metadata = os.stat(‘feed.xml’)

[2]

>>> metadata.st_mtime
1247520344.9537716

[3]
[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. feed.xml — файл в папке с примерами. Вызов функции os.stat() возвращает объект, содержащий различные метаданные о файле.
  3. ↑ st_mtime — время изменения файла, но записано оно в ужасно неудобном формате. (Фактически это количество секунд, прошедших с начала «эры UNIX», начавшейся в первую секунду 1 января 1970 года. Серьёзно.)
  4. ↑ Модуль time является частью стандартной библиотеки Python. Он содержит функции для преобразований между различными форматами представления времени и часовыми поясами, для преобразования их в строки (str) и др.
  5. ↑ Функция time.localtime() преобразует время из формата «секунды с начала эры» (поле st_mtime, возвращённое функцией os.stat()) в более удобную структуру, содержащую год, месяц, день, час, минуту, секунду и т. д. Этот файл в последний раз изменялся 13 июля 2009 года, примерно в 17 часов, 25 минут.

# продолжение предыдущего примера

>>> metadata.st_size
3070
>>> import humansize

[1]

>>> humansize.approximate_size(metadata.st_size)
‘3.0 KiB’

[2]
  1. ↑ Функция os.stat() также возвращает размер файла в свойстве st_size. Размер файла feed.xml — 3070 байт.
  2. ↑ Вы можете передать свойство st_size в функцию approximate_size().

Получение абсолютных путей

В предыдущем разделе функция glob.glob() возвращала список относительных путей. В первом примере пути имели вид ‘examples\feed.xml’, а во втором относительные пути были даже короче, например, ‘romantest1.py’. Пока вы остаётесь в текущем рабочем каталоге, по этим относительным путям можно будет открывать файлы или получать их метаданные. Но если вы захотите получить абсолютный путь — то есть тот, который включает все имена каталогов до корневого или до буквы диска, вам понадобится функция 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

Генераторы списков

В генераторах списков можно использовать любые выражения Python.

С помощью генераторов списков можно легко отобразить один список в другой, применив некоторую функцию к каждому элементу.

>>> a_list = [1, 9, 8, 4]
>>> [elem * 2 for elem in a_list]
[2, 18, 16, 8]

[1]
[2]

>>> a_list = [elem * 2 for elem in a_list]
>>> a_list
[2, 18, 16, 8]

[3]
  1. ↑ Чтобы понять, что здесь происходит, прочитайте генератор справа налево. a_list — отображаемый список. Python последовательно перебирает элементы списка a_list, временно присваивая значение каждого элемента переменной elem. Затем применяет функцию elem * 2 и добавляет результат в возвращаемый список.
  2. ↑ Генератор создаёт новый список, не изменяя исходный.
  3. ↑ Можно присвоить результат работы генератора списка отображаемой переменной. Python создаст новый список в памяти и, когда результат работы генератора будет получен, присвоит его исходной переменной.

В генераторах списков можно использовать любые выражения 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. ↑ Это выражение возвращает список всех .xml-файлов в текущем рабочем каталоге.
  2. ↑ Этот генератор принимает список всех .xml-файлов и преобразует его в список полных путей.

При генерировании списков можно также фильтровать элементы, чтобы отбросить некоторые значения.

>>> [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]
  1. ↑ Чтобы профильтровать список, добавьте оператор if в конце генератора списка. Выражение, стоящее после оператора if, будет вычислено для каждого элемента списка. Если это выражение будет истинно, данный элемент будет обработан и включён в генерируемый список. В данной строке генерируется список всех .py-файлов в текущей директории, а оператор if фильтрует этот список, оставляя только файлы размером больше 6000 байт. Таких файлов только шесть, поэтому будет сгенерирован список из шести имён файлов.

Все рассмотренные примеры генераторов списков использовали простые выражения: умножение числа на константу, вызов одной функции или просто возврат элемента списка без изменений (после фильтрации). Но при генерации списков можно использовать выражения любой сложности.

>>> [(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-файлы в текущем рабочем каталоге, получает размер каждого файла (вызывая функцию os.stat()), и создает кортеж из размера файла и абсолютного пути каждого файла (вызывая функцию os.path.realpath()).
  2. ↑ Этот генератор, основанный на предыдущем, вызывает функцию approximate_size(), передавая ей размер каждого .xml-файла.

Генераторы словарей

Генератор словаря похож на генератор списка, но вместо списка он создает словарь.

>>> 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,
 st_mtime=1247520344, st_ctime=1247520344))

[2]

>>> metadata_dict = {f:os.stat(f) for f in glob.glob(‘*test*.py’)}

[3]

>>> type(metadata_dict)
<class ‘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, проверяет их имена, а затем создает кортеж из имени файла и метаданных файла (вызывая функцию os.stat()).
  2. ↑ Каждый элемент результирующего списка — кортеж.
  3. ↑ Это генератор словаря. Синтаксис подобен синтаксису генератора списка, но с двумя отличиями. Во-первых, он заключён в фигурные скобки, а не в квадратные. Во-вторых, вместо одного выражения для каждого элемента он содержит два, разделённые двоеточием. Выражение слева от двоеточия (в нашем примере f) является ключом словаря; выражение справа от двоеточия (в нашем примере os.stat(f)) — значением.
  4. ↑ Генератор словаря возвращает словарь.
  5. ↑ Ключи данного словаря — это просто имена файлов, полученные с помощью glob.glob(‘*test*.py’).
  6. ↑ Значение, связанное с каждым ключом, получено с помощью функции os.stat(). Это означает, что в этом словаре мы можем по имени файла получить его метаданные. Один из элементов метаданных (st_size) — это размер файла. Размер файла alphameticstest.py — 2509 байт.

Также, как и в генераторах списков, вы можете включать в генераторы словарей условие if, чтобы отфильтровать входную последовательность с помощью выражения-условия, вычисляющегося для каждого элемента.

>>> 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) \
…                   for f, meta in metadata_dict.items() if meta.st_size > 6000}

[2]

>>> list(humansize_dict.keys())
[‘romantest9’, ‘romantest8’, ‘romantest7’, ‘romantest6’, ‘romantest10’, ‘pluraltest6’]

[3]

>>> humansize_dict[‘romantest9’]
‘6.5 KiB’

[4]
  1. ↑ В этом выражении берётся список файлов в текущей директории (glob.glob(‘*’)), для каждого файла определяются его метаданные (os.stat(f)) и строится словарь, ключами которого выступают имена файлов, а значениями — метаданные каждого файла.
  2. ↑ Этот генератор строится на основе предыдущего. Отфильтровываются файлы меньше 6000 байт (if meta.st_size > 6000). Отобранные элементы используются для построения словаря, ключами которого являются имена файлов без расширения (os.path.splitext(f)[0]), а значениями — приблизительный размер каждого файла (humansize.approximate_size(meta.st_size)).
  3. ↑ Как вам уже известно из предыдущего примера, всего имеется шесть таких файлов, следовательно, в этом словаре шесть элементов.
  4. ↑ Значение для каждого ключа представляется из себя строку, полученную вызовом функции approximate_size().

Другие интересные штуки, которые можно делать с помощью генераторов словарей

Вот трюк с генераторами словарей, который когда-нибудь может оказаться полезным: перестановка местами ключей и значений словаря.

>>> 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 «<stdin>», line 1, in <module>
  File «<stdin>», line 1, in <dictcomp>
TypeError: unhashable type: ‘list’

Генераторы множеств

Нельзя оставить за бортом и множества, они тоже могут создаваться с помощью генераторов. Единственное отличие — вместо пар ключ:значение, они строятся на основе одних значений.

>>> 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. ↑ В качестве входных данных генераторы множеств могут получать другие множества. Этот генератор рассчитывает квадраты множества чисел в диапазоне от 0 до 9.
  2. ↑ Подобно генераторам списков и словарей, генераторы множеств могут содержать условие if для проверки каждого элемента перед включением его в результирующее множество.
  3. ↑ На вход генераторы множеств могут принимать не только множества, но и любые другие последовательности.

Материалы для дальнейшего чтения

Глава 1. Ваша первая программа на Python

Добавлено 2 апреля 2020 в 21:12

Сохранить или поделиться

Не убегайте от проблем, не осуждайте себя и не несите своё бремя в праведном безмолвии. У вас есть проблема? Прекрасно! Это пойдёт на пользу! Радуйтесь: погрузитесь в неё и исследуйте!

Досточтимый Хенепола Гунаратана

Содержание главы

Погружение

Обычно книги о программировании начинаются с кучи скучных глав о базовых вещах и постепенно переходят к созданию чего-нибудь полезного. Давайте всё это пропустим. Вот вам готовая, работающая программа на Python. Возможно, вы ровным счётом ничего в ней не поймёте. Не беспокойтесь об этом, скоро мы разберём её строчка за строчкой. Но сначала прочитайте код и посмотрите, что вы сможете извлечь из него.

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):
    '''Преобразует размер файла в удобочитаемую для человека форму.

    Ключевые аргументы:
    size -- размер файла в байтах
    a_kilobyte_is_1024_bytes -- если True (по умолчанию), используются степени 1024
                                если False, используются степени 1000

    Возвращает: текстовую строку (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 это будет выглядеть примерно так:

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

Что сейчас произошло? Вы выполнили свою первую программу на Python. Вы вызвали интерпретатор Python в командной строке и передали ему имя скрипта, который хотели выполнить. В скрипте определена функция approximate_size(), которая принимает точный размер файла в байтах и вычисляет «красивый» (но приблизительный) размер. (Возможно, вы видели его в Проводнике Windows, в Mac OS X Finder, в Nautilus, Dolphin или Thunar в Linux. Если отобразить папку с документами в виде таблицы, файловый менеджер в каждой её строке покажет иконку, название документа, размер, тип, дату последнего изменения и т. д. Если в папке есть 1093-байтовый файл с названием «TODO», файловый менеджер не покажет «TODO 1093 байта»; вместо этого он скажет что-то типа «TODO 1 КБ». Именно это и делает функция approximate_size().)

Посмотрите на последние строки скрипта, вы увидите два вызова print(approximate_size(аргументы)). Это вызовы функций. Сначала вызывается approximate_size(), которой передаётся несколько аргументов, а затем возвращённое ею значение берётся и передаётся прямо в функцию print(). Функция print() встроенная, вы нигде не найдёте её явного объявления. Её можно только использовать, где угодно и когда угодно. (Есть множество встроенных функций, и ещё больше функций, которые выделены в отдельные модули. Только терпение, непоседа.)

Итак, почему при выполнении скрипта в командной строке, всегда получается один и тот же результат? Мы ещё дойдём до этого. Но сначала давайте посмотрим на функцию approximate_size().

1.2 Объявление функций

Когда вам нужна функция, просто объявите её.

В Python есть функции, как и в большинстве других языков, но нет ни отдельных заголовочных файлов, как в C++, ни конструкций interface/implementation, как в Паскале. Когда вам нужна функция, просто объявите её, например, так:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):

Объявление начинается с ключевого слова def, затем следует имя функции, а за ним аргументы в скобках. Если аргументов несколько, они разделяются запятыми.

К тому же, стоит заметить, что в объявлении функции не задаётся тип возвращаемых данных. Функции в Python не определяют тип возвращаемых ими значений; они даже не указывают, существует ли возвращаемое значение вообще. (На самом деле, любая функция в Python возвращает значение; если в функции выполняется инструкция return, она возвращает указанное в этой инструкции значение, если нет – возвращает None – специальное нулевое значение.)

В некоторых языках программирования функции (возвращающие значение) объявляются ключевым словом function, а подпрограммы (не возвращающие значений) – ключевым словом sub. В Python же подпрограмм нет. Все функции возвращают значение (даже если оно None), и всегда объявляются ключевым словом def.

Функция approximate_size() принимает два аргумента: size и a_kilobyte_is_1024_bytes, но ни один из них не имеет типа. В Python тип переменных никогда не задаётся явно. Python вычисляет тип переменной и следит за ним самостоятельно.

В Java и других языках со статической типизацией необходимо указывать тип возвращаемого значения и каждого аргумента функции. В Python явно указывать тип, для чего-либо, не нужно. Python самостоятельно отслеживает типы переменных на основе присваиваемых им значений.

1.2.1 Необязательные и именнованные аргументы

В Python аргументы функций могут иметь значения по умолчанию; если функция вызывается без аргумента, то он принимает своё значение по умолчанию. К тому же, аргументы можно указывать в любом порядке, задавая их имена.

Давайте ещё раз посмотрим на объявление функции approximate_size():

def approximate_size(size, a_kilobyte_is_1024_bytes=True):

Второй аргумент – a_kilobyte_is_1024_bytes – записывается со значением по умолчанию True. Это означает, что этот аргумент необязательный; можно вызвать функцию без него, а Python будет действовать так, как будто она вызвана с True в качестве второго параметра.

Теперь взглянем на последние строки скрипта:

if __name__ == '__main__':
    print(approximate_size(1000000000000, False))  ①
    print(approximate_size(1000000000000))         ②
  1. Строка 27. Функция approximate_size() вызывается с двумя аргументами. Внутри функции approximate_size() переменная a_kilobyte_is_1024_bytes будет False, поскольку False передаётся явно во втором аргументе.
  2. Строка 28. Функция approximate_size() вызывается только с одним аргументом. Но всё в порядке, потому что второй аргумент необязателен! Поскольку второй аргумент не указан, он принимает значение по умолчанию True, как определено в объявлении функции.

А ещё можно передавать значения в функцию по имени.

>>> 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)  ③
'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, 4000)       ④
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
>>> approximate_size(size=4000, False)                           ⑤
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg

Перевод сообщений оболочки

  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg

Файл «<stdin>», строка 1

SyntaxError: неименованный аргумент после именованного

  1. Строка 2. Функция approximate_size() вызывается со значением 4000 в первом аргументе и False в аргументе по имени a_kilobyte_is_1024_bytes. (Он стоит на втором месте, но это не важно, как вы скоро увидите.)
  2. Строка 4. Функция approximate_size() вызывается со значением 4000 в аргументе по имени size и False в аргументе по имени a_kilobyte_is_1024_bytes. (Эти именованные аргументы стоят в том же порядке, в каком они перечислены в объявлении функции, но это тоже не важно.)
  3. Строка 6. Функция approximate_size() вызывается с False в аргументе по имени a_kilobyte_is_1024_bytes и 4000 в аргументе по имени size. (Видите? Я же говорил, что порядок не важен.)
  4. Строка 8. Этот вызов не работает, потому что за именованным аргументом следует неименованный (позиционный). Если читать список аргументов слева направо, то как только встречается именованный аргумент, все следующие за ним аргументы тоже должны быть именованными.
  5. Строка 11. Этот вызов тоже не работает, по той же причине, что и предыдущий. Удивительно? Ведь сначала передаётся 4000 в аргументе по имени size, затем, «очевидно», можно ожидать, что False станет аргументом по имени a_kilobyte_is_1024_bytes. Но в Python это не работает. Раз есть именованный аргумент, все аргументы справа от него тоже должны быть именованными.

1.3 Написание читаемого кода

Не буду растопыривать перед вами пальцы и мучить длинной лекцией о важности документирования кода. Просто знайте, что код пишется один раз, а читается многократно, и самый важный читатель вашего кода – это вы сами через полгода после написания (т. е. всё уже забыто, и вдруг понадобилось что-то починить). В Python писать читаемый код просто. Используйте это его преимущество и через полгода вы скажете мне «спасибо».

1.3.1 Строки документации

Каждая функция заслуживает хорошую документацию.

Функции в Python можно документировать, снабжая их строками документации (англ. documentation string, сокращённо docstring). В нашей программе у функции approximate_size() есть строка документации:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    '''Преобразует размер файла в удобочитаемую для человека форму.

    Ключевые аргументы:
    size -- размер файла в байтах
    a_kilobyte_is_1024_bytes -- если True (по умолчанию), используются степени 1024
                                если False, используются степени 1000

    Возвращает: текстовую строку (string)

    '''

Тройные кавычки (три апострофа) используются для задания строк (имеется ввиду тип данных string) содержащих многострочный текст. Всё, что находится между начальными и конечными кавычками, является частью одной строки данных, включая переводы строк, пробелы в начале каждой строки текста, и другие кавычки. Вы можете использовать их где угодно, но чаще всего будете встречать их в определениях строк документации.

Тройные кавычки – это ещё и простой способ определить строку, содержащую одинарные (апострофы) и двойные кавычки, подобно qq/.../ в Perl 5.

Всё, что находится в тройных кавычках, – это строка документации функции, описывающая, что делает эта функция. Строка документации, если она есть, должна начинать тело функции, т. е. находится на следующей строчке сразу под объявлением функции. Строго говоря, вы не обязаны писать документацию к каждой своей функции, но всегда желательно это делать. Я знаю, что вам уже все уши прожужжали про документирование кода, но Python даёт вам дополнительный стимул – строки документации доступны во время выполнения как атрибут функции.

Многие IDE для Python используют строки документации для отображения контекстной справки, и когда вы набираете название функции, её документация появляется во всплывающей подсказке. Это может быть невероятно полезно, но это всего лишь строки документации, которые вы сами пишете.

1.4 Путь поиска оператора import

Перед тем, как идти дальше, я хочу вкратце рассказать о путях поиска библиотек. Когда вы пытаетесь импортировать модуль (с помощью оператора import), Python ищет его в нескольких местах. В частности, он ищет во всех директориях, перечисленных в sys.path. Это просто список, который можно легко просматривать и изменять при помощи стандартных списочных методов. (Вы узнаете больше о списках в главе «Встроенные типы данных».)

>>> 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                                                        ③
<module 'sys' (built-in)>
>>> 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']
  1. Строка 1. Импортирование модуля sys делает доступными все его функции и атрибуты.
  2. Строка 2.sys.path – список имён директорий, определяющий текущий путь поиска. (Ваш будет выглядеть иначе, в зависимости от вашей операционной системы, от используемой версии Python и от того, куда он был установлен.) Python будет искать в этих директориях (в заданном порядке) файл с расширением «.py», имя которого совпадает с тем, что вы пытаетесь импортировать.
  3. Строка 10. Вообще-то я вас обманул; истинное положение дел немного сложнее, потому что не все модули лежат в файлах с расширением «.py». Некоторые из них, как, например, модуль sys, являются встроенными; они впаяны в сам Python. Встроенные модули ведут себя точно так же, как обычные, но их исходный код недоступен, потому что они не были написаны на Python! (Модуль sys написан на Си.)
  4. Строка 12. Можно добавить новую директорию в путь поиска, добавив имя директории в список sys.path, во время выполнения Python, и тогда Python будет просматривать её наравне с остальными, как только вы попытаетесь импортировать модуль. Новый путь поиска будет действителен в течение всего сеанса работы Python.
  5. Строка 13. Выполнив команду sys.path.insert(0, новый_путь), вы вставили новую директорию на первое место в список sys.path, и, следовательно, в начало пути поиска модулей. Почти всегда, именно это вам и нужно. В случае конфликта имён (например, если Python поставляется со 2-й версией некоторой библиотеки, а вы хотите использовать версию 3) этот приём гарантирует, что будут найдены и использованы ваши модули, а не те, которые идут в комплекте с Python.

1.5 Всё является объектом

Если вы вдруг пропустили, я только что сказал, что функции в Python имеют атрибуты, и эти атрибуты доступны во время выполнения. Функция, как и всё остальное в Python, является объектом.

Запустите интерактивную оболочку Python и повторите за мной:

>>> import humansize                               ①
>>> print(humansize.approximate_size(4096, True))  ②
4.0 KiB
>>> print(humansize.approximate_size.__doc__)      ③
Преобразует размер файла в удобочитаемую для человека форму.

    Ключевые аргументы:
    size -- размер файла в байтах
    a_kilobyte_is_1024_bytes -- если True (по умолчанию), используются степени 1024
                                если False, используются степени 1000

    Возвращает: текстовую строку (string)
  1. Строка 1. Первая строка импортирует программу humansize в качестве модуля – фрагмента кода, который можно использовать интерактивно или из другой Python-программы. После того, как модуль был импортирован, можно обращаться ко всем его публичным функциям, классам и атрибутам. Импорт применяется как в модулях, для доступа к функциональности других модулей, так и в интерактивной оболочке Python. Это очень важная идея, и вы ещё не раз встретите её на страницах этой книги.
  2. Строка 2. Когда вы хотите использовать функцию, определённую в импортированном модуле, нужно дописать к её имени название модуля. То есть вы не можете использовать просто approximate_size, обязательно humansize.approximate_size. Если вы использовали классы в Java, то для вас это должно быть знакомо.
  3. Строка 4. Вместо того, чтобы вызвать функцию (как вы, возможно, ожидали), вы запросили один из её атрибутов – __doc__.

Оператор import в Python похож на require из Perl. После import в Python, вы обращаетесь к функциям модуля как модуль.функция; после require в Perl, для обращения к функциям модуля используется имя модуль::функция.

1.5.1 Что такое объект?

В языке Python всё является объектом, и у любого объекта могут быть атрибуты и методы. Все функции имеют стандартный атрибут __doc__, содержащий строку документации, определённую в исходном коде функции. Модуль sys – тоже объект, имеющий (кроме прочего) атрибут под названием path. И так далее.

Но мы так и не получили ответ на главный вопрос: что такое объект? Разные языки программирования определяют «объект» по-разному. В одних считается, что все объекты должны иметь атрибуты и методы. В других, что объекты могут порождать подклассы. В Python определение ещё менее чёткое. Некоторые объекты не имеют ни атрибутов, ни методов, хотя и могли бы их иметь. Не все объекты порождают подклассы. Но всё является объектом в том смысле, что может быть присвоено переменной или передано функции в качестве аргумента.

Возможно, вы встречали термин «объект первого класса» в других книгах о программировании. В Python функции – объекты первого класса. Функцию можно передать в качестве аргумента другой функции. Модули – объекты первого класса. Весь модуль целиком можно передать в качестве аргумента функции. Классы – объекты первого класса, и отдельные их экземпляры – тоже объекты первого класса.

Это очень важно, поэтому я повторю это, на случай если вы пропустили первые несколько раз: всё в Python является объектом. Строки – это объекты. Списки – объекты. Функции – объекты. Классы – объекты. Экземпляры классов – объекты. И даже модули являются объектами.

1.6 Отступы

Функции в Python не имеют ни явных указаний begin и end, ни фигурных скобок, которые бы показывали, где код функции начинается, а где заканчивается. Разделители – только двоеточие (:) и отступы самого кода.

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('число слишком большое')
  1. Строка 1. Блоки кода определяются по их отступам. Под «блоками кода» я подразумеваю функции, блоки if, циклы for и while и т. д. Увеличение отступа начинает блок, а уменьшение – заканчивает. Ни скобок, ни ключевых слов. Это означает, что важное значение имеют пробелы, и их количество тоже. В этом примере код функции имеет отступ в четыре пробела. Здесь не обязательно должно быть именно четыре пробела, просто их число должно быть постоянным. Первая встретившаяся строчка без отступа будет означать конец функции.
  2. Строка 2. За оператором if должен следовать блок кода. Если в результате вычисления условного выражения оно окажется истинным, то выполнится блок, выделенный отступом, в противном случае произойдёт переход к блоку else (если он есть). Обратите внимание, что нет скобок вокруг выражения.
  3. Строка 3. Эта строка находится внутри блока if. Оператор raise вызывает исключение (типа ValueError), но только если size < 0.
  4. Строка 4. Это ещё не конец функции. Совсем пустые строки не считаются. Они могут повысить читаемость кода, но не могут служить разделителями блоков кода. Блок кода функции продолжается на следующей строке.
  5. Строка 6. Оператор цикла for тоже начинает блок кода. Блоки кода могут содержать несколько строк, а именно – столько, сколько строк имеют такую же величину отступа. Этот цикл for содержит три строки кода. Других синтаксических конструкций для описания многострочных блоков кода нет. Просто делайте отступы, и будет вам счастье!

После того, как вы переборете внутренние противоречия и проведёте пару ехидных аналогий с Фортраном, вы подружитесь с отступами и начнёте видеть их преимущества. Одно из главных преимуществ – то, что все программы на Python выглядят примерно одинаково, поскольку отступы – требование языка, а не вопрос стиля. Благодаря этому становится проще читать и понимать код на Python, написанный другими людьми.

В Python используются символы возврата каретки для разделения операторов, а также двоеточие и отступы для разделения блоков кода. В C++ и Java используются точки с запятой для разделения операторов и фигурные скобки для блоков кода.

1.7 Исключения

Исключения (англ. exceptions – нештатные, исключительные ситуации, требующие специальной обработки) используются в Python повсюду. Буквально каждый модуль в стандартной библиотеке Python использует их, да и сам Python вызывает их во многих ситуациях. Вы ещё неоднократно встретите их на страницах этой книги.

Что такое исключение? Обычно это ошибка, признак того, что что-то пошло не так. (Не все исключения являются ошибками, но пока это не важно.) В некоторых языках программирования принято возвращать код ошибки, который вы потом проверяете. В Python принято использовать исключения, которые вы обрабатываете.

Когда происходит ошибка в оболочке Python, она выводит кое-какие подробности об исключении и о том, как это случилось, и всё. Это называется необработанным исключением. Когда это исключение было вызвано, не нашлось никакого программного кода, чтобы заметить его и обработать должным образом, поэтому оно всплыло на самый верхний уровень – в оболочку Python, которая вывела немного отладочной информации и успокоилась. В оболочке это не так уж страшно, однако если это произойдёт во время работы настоящей программы, то, если исключение не будет обработано, вся программа с грохотом упадёт. Может быть это то, что вам нужно, а может, и нет.

В отличие от Java, функции в Python не содержат объявлений о том, какие исключения они могут вызывать. Вам решать, какие из возможных исключений необходимо отлавливать.

Результат исключения – это не всегда полный крах программы. Исключения можно обработать. Иногда исключения возникают из-за настоящих ошибок в вашем коде (например, доступ к переменной, которая не существует), но порой исключение – это нечто, что вы можете предвидеть. Если вы открываете файл, он может не существовать. Если вы импортируете модуль, он может быть не установлен. Если вы подключаетесь к базе данных, она может быть недоступна или у вас может быть недостаточно прав для доступа к ней. Если вы знаете, что какая-то строка кода может вызвать исключение, то его следует обработать с помощью блока try...except.

Python использует для обработки исключений блоки try...except, а для их генерации – оператор raise. Java и C++ используют для обработки исключений блоки try...catch, а для их генерации – оператор throw.

Функция approximate_size() вызывает исключение в двух разных случаях: если переданный ей размер (size) больше, чем функция может обработать, или если он меньше нуля.

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

Синтаксис вызова исключений достаточно прост. Надо написать оператор raise, за ним название исключения и опционально, поясняющую строку для отладки. Синтаксис напоминает вызов функции. (На самом деле, исключения реализованы как классы, и оператор raise просто создаёт экземпляр класса ValueError и передаёт в его метод инициализации строку ‘число должно быть неотрицательным‘. Но мы забегаем вперёд!)

Нет необходимости обрабатывать исключение в той функции, которая его вызвала. Если одна функция не обработает его, исключение передаётся в функцию, вызвавшую эту, затем в функцию, вызвавшую вызвавшую, и т. д. «вверх по стеку». Если исключение нигде не будет обработано, то программа упадёт, а Python выведет «раскрутку стека» (англ. traceback) в стандартный поток ошибок – и на этом конец. Повторяю, возможно, это именно то, что вам нужно, – это зависит от того, что делает ваша программа.

1.7.1 Отлов ошибок импорта

Одно из встроенных исключений Python – ImportError (ошибка импорта), которое вызывается, если не удаётся импортировать модуль. Это может случиться по нескольким причинам, самая простая из которых – отсутствие модуля в пути поиска, оператора import. Вы можете использовать это для включения в программу опциональных возможностей. Например, библиотека chardet предоставляет возможность автоматического определения кодировки символов. Предположим, ваша программа хочет использовать эту библиотеку в том случае, если она есть, или спокойно продолжить работу, если пользователь не установил её. Можно сделать это с помощью блока try...except.

try:
  import chardet
except ImportError:
  chardet = None

После этого можно проверять наличие модуля chardet простым if:

if chardet:
  # что-то сделать
else:
  # продолжить дальше

Другое частое применение исключения ImportError – выбор из двух модулей, предоставляющих одинаковый интерфейс (API), причём применение одного из них предпочтительнее другого (может, он быстрее работает или требует меньше памяти). Для этого можно попытаться импортировать сначала один модуль, и если это не удалось , то импортировать другой. К примеру, в главе XML рассказывается о двух модулях, реализующих один и тот же API, так называемый ElementTree API. Первый – lxml – сторонний модуль, который необходимо скачивать и устанавливать самостоятельно. Второй – xml.etree.ElementTree – медленнее, но входит в стандартную библиотеку Python 3.

try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

При выполнении этого блока try...except будет импортирован один из двух модулей под именем etree. Поскольку оба модуля реализуют один и тот же API, то в последующем коде нет необходимости проверять, какой из этих модулей был импортирован. И раз импортированный модуль в любом случае именуется как etree, то не придётся вставлять лишние if для обращения к разноимённым модулям.

1.8 Несвязанные переменные

Взглянем ещё раз на вот эту строку функции approximate_size():

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000

Мы нигде не объявляли переменную multiple (множитель), мы только присвоили ей значение. Всё в порядке, Python позволяет так делать. Что он не позволит сделать, так это обратиться к переменной, которой не было присвоено значение. Если попытаться так сделать, возникнет исключение NameError (ошибка в имени).

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = 1
>>> x
1

Перевод сообщения оболочки

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Раскрутка стека (список последних вызовов):

Файл «<stdin>», строка 1, <модуль>

NameError: имя ‘x’ не определено

Однажды вы скажете Python «спасибо» за это.

1.9 Всё чувствительно к регистру

Все имена в Python чувствительны к регистру – имена переменных, функций, классов, модулей, исключений. Всё, что можно прочитать, записать, вызвать, создать или импортировать, чувствительно к регистру.

>>> an_integer = 1
>>> an_integer
1
>>> AN_INTEGER
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'AN_INTEGER' is not defined
>>> An_Integer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'An_Integer' is not defined
>>> an_inteGer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'an_inteGer' is not defined

Перевод сообщений оболочки

Раскрутка стека (список последних вызовов):

Файл «<stdin>», строка 1, <модуль>

NameError: имя ‘<имя>’ не определено

1.10 Запуск скриптов

В Python всё является объектом.

Модули Python – это объекты, имеющие несколько полезных атрибутов. И это обстоятельство можно использовать для простого тестирования модулей, при их написании, путём включения специального блока кода, который будет выполняться при запуске файла из командной строки. Взгляните на последние строки humansize.py:

if __name__ == '__main__':
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

Как и в C, в Python оператор == используется для проверки на равенство, а оператор = используется для присваивания. Но в отличие от C, Python не поддерживает присваивание внутри другого выражения, поэтому у вас не получится случайно присвоить значение вместо проверки на равенство.

Итак, что же делает этот блок if особенным? У всех модулей, как у объектов, есть встроенный атрибут __name__ (имя). И значение этого атрибута зависит от того, как модуль используется. Если модуль импортируется, то __name__ принимает значение равное имени файла модуля, без расширения и пути к каталогу.

>>> import humansize
>>> humansize.__name__
'humansize'

Но модуль можно запустить и напрямую, как самостоятельную программу, в этом случае __name__ примет специальное значение по умолчанию, __main__. Python вычислит значение условного выражения в операторе if, определит его истинность, и выполнит блок кода if. В данном случае, будут напечатаны два значения.

c:\home\diveintopython3> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

И это ваша первая программа на Python!

1.11 Материалы для дальнейшего чтения

Источник:

  • Mark Pilgrim. Dive Into Python 3

Сохранить или поделиться

Ваша первая программа на Python — погрузитесь в Python 3

Ваша первая программа на Python — погрузитесь в Python 3

Вы здесь: Главная ‣ Погрузитесь в Python 3 ‣

Уровень сложности: ♦ ♢♢♢♢

❝ Не ​​хорони свою ношу в святом молчании. У тебя проблемы? Отлично. Радуйтесь, ныряйте и исследуйте. №
— Дост. Хенепола Гунаратана

Дайвинг в

Конвенция

гласит, что я должен утомить вас фундаментальными строительными блоками программирования, чтобы мы могли медленно работать над созданием чего-то полезного.Давай пропустим все это. Вот полная работающая программа на Python. Возможно, для вас это не имеет никакого смысла. Не беспокойтесь об этом, потому что вы будете разбирать это строка за строкой. Но сначала прочтите его и посмотрите, что вы можете с этим поделать.

[загрузить humanize.py ]

  СУФФИКСОВ = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):
    '' 'Преобразование размера файла в удобочитаемую форму.Аргументы ключевого слова:
    size - размер файла в байтах
    a_kilobyte_is_1024_bytes - если True (по умолчанию), использовать кратные 1024
                                если False, используйте число, кратное 1000

    Возвращает: строка

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

    multiple = 1024, если a_kilobyte_is_1024_bytes иначе 1000
    для суффикса в СУФФИКСАХ [несколько]:
        size / = несколько
        если размер <несколько:
            вернуть '{0: .1f} {1}'. формат (размер, суффикс)

    поднять ValueError ('слишком большое число')

если __name__ == '__main__':
    print (приблизительный_размер (1000000000000; Ложь))
    печать (приблизительный_размер (1000000000000))  

Теперь давайте запустим эту программу из командной строки.В Windows это будет выглядеть примерно так:

  c: \ home \ diveintopython3 \ examples>   c: \ python31 \ python.exe humanize.py 
  1,0 ТБ
931,3 ГиБ  

В Mac OS X или Linux это выглядело бы примерно так:

  you @ localhost: ~ / diveintopython3 / examples $   python3 humanize.py 
  1,0 ТБ
931,3 ГиБ  

Что только что произошло? Вы выполнили свою первую программу на Python. Вы вызвали интерпретатор Python в командной строке и передали имя сценария, который должен выполнить Python.Сценарий определяет единственную функцию, функцию corrective_size () , которая принимает точный размер файла в байтах и ​​вычисляет «красивый» (но приблизительный) размер. (Вы, вероятно, видели это в проводнике Windows, или в Mac OS X Finder, или в Nautilus, или Dolphin, или в Thunar в Linux. Если вы отобразите папку с документами в виде многоколоночного списка, отобразится таблица со значком документа , имя документа, размер, тип, дату последнего изменения и т. д. Если папка содержит файл размером 1093 байта с именем TODO , ваш файловый менеджер не будет отображать TODO 1093 байта ; он скажет что-то вроде TODO 1 KB вместо этого.Это то, что делает функция приближаться к размеру () .)

Посмотрите на нижнюю часть скрипта, и вы увидите два вызова print (приблизительный_размер ( аргументов )) . Это вызовы функций: сначала вызывается функция приближаться к размеру () и передается ряд аргументов, затем берется возвращаемое значение и передается прямо в функцию print () . Функция print () является встроенной; вы никогда не увидите явного объявления этого.Вы можете просто использовать его в любое время и в любом месте. (Есть много встроенных функций, и гораздо больше функций, которые разделены на модулей . Терпение, кузнечик.)

Так почему же выполнение сценария в командной строке каждый раз дает один и тот же результат? Мы к этому еще вернемся. Во-первых, давайте посмотрим на функцию apply_size () .

Объявление функций

Python имеет функции, как и большинство других языков, но у него нет отдельных файлов заголовков, таких как C ++ или интерфейс , реализация /, разделы , такие как Pascal.Когда вам нужна функция, просто объявите ее, например:

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):  

Когда вам нужна функция, просто объявите ее.

Ключевое слово def запускает объявление функции, за которым следует имя функции, за которым следуют аргументы в круглых скобках. Несколько аргументов разделяются запятыми.

Также обратите внимание, что функция не определяет возвращаемый тип данных. Функции Python не указывают тип возвращаемого значения; они даже не указывают, возвращают ли они значение.(Фактически, каждая функция Python возвращает значение; если функция когда-либо выполняет оператор return , она вернет это значение, в противном случае она вернет None , нулевое значение Python.)

☞На некоторых языках функции (возвращающие значение) начинаются с , функция , а подпрограммы (которые не возвращают значение) начинаются с sub . В Python нет подпрограмм. Все является функцией, все функции возвращают значение (даже если это None ), и все функции начинаются с def .

Функция приближаться к размеру () принимает два аргумента - size и a_kilobyte_is_1024_bytes - но ни один из аргументов не определяет тип данных. В Python переменные никогда не вводятся явно. Python определяет, к какому типу относится переменная, и отслеживает ее внутренне.

☞В Java и других языках со статической типизацией необходимо указать тип данных возвращаемого значения функции и каждого аргумента функции. В Python вы никогда не указываете явно тип данных для чего-либо.В зависимости от того, какое значение вы назначаете, Python отслеживает тип данных внутри себя.

Необязательные и именованные аргументы

Python позволяет аргументам функции иметь значения по умолчанию; если функция вызывается без аргумента, аргумент получает значение по умолчанию. Кроме того, аргументы можно указывать в любом порядке с помощью именованных аргументов.

Давайте еще раз посмотрим на это объявление функции приближенно_size () :

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):  

Второй аргумент, a_kilobyte_is_1024_bytes , указывает значение по умолчанию True .Это означает, что аргумент необязательный ; вы можете вызвать функцию без него, и Python будет действовать так, как если бы вы вызвали ее с True в качестве второго параметра.

Теперь посмотрим на нижнюю часть скрипта:

  если __name__ == '__main__':
    print (приблизительный_размер (1000000000000, Ложь)) ①
    печать (приблизительный_размер (1000000000000)) ②  
  1. Это вызывает функцию приближаться к размеру () с двумя аргументами. В функции приближаться к размеру () , a_kilobyte_is_1024_bytes будет False , поскольку вы явно передали False в качестве второго аргумента.
  2. Это вызывает функцию приближаться к размеру () только с одним аргументом. Но это нормально, потому что второй аргумент не обязателен! Поскольку вызывающий не указывает, второй аргумент по умолчанию равен True , как определено в объявлении функции.

Вы также можете передавать значения в функцию по имени.

  >>>   из человеческого размера импорт приблизительно 
  >>>   приблизительный_размер (4000, a_kilobyte_is_1024_bytes = False)  '4.0 КБ '
  >>>   приблизительный_размер (размер = 4000, a_kilobyte_is_1024_bytes = False)  '4.0 КБ' 
  >>>   приблизительный_размер (a_kilobyte_is_1024_bytes = False, size = 4000)  '4,0 КБ' 
  >>>   приблизительный_размер (a_kilobyte_is_1024_bytes = False, 4000)  Файл "", строка 1
SyntaxError: аргумент без ключевого слова после аргумента ключевого слова 
  >>>   приблизительный_размер (размер = 4000, False)  Файл "", строка 1
SyntaxError: аргумент без ключевого слова после аргумента ключевого слова  
  1. Это вызывает функцию приближаться к размеру () с 4000 для первого аргумента ( размер ) и False для аргумента с именем a_kilobyte_is_1024_bytes .(Это второй аргумент, но не имеет значения, как вы увидите через минуту.)
  2. Это вызывает функцию приближаться к размеру () с 4000 для аргумента с именем size и False для аргумента с именем a_kilobyte_is_1024_bytes . (Эти именованные аргументы оказываются в том же порядке, что и аргументы, перечисленные в объявлении функции, но это тоже не имеет значения.)
  3. Это вызывает функцию приближаться к размеру () с False для аргумента с именем a_kilobyte_is_1024_bytes и 4000 для аргумента с именем размер .(Видите? Я сказал вам, что порядок не имеет значения.)
  4. Этот вызов завершается ошибкой, потому что у вас есть именованный аргумент, за которым следует безымянный (позиционный) аргумент, и это никогда не работает. При чтении списка аргументов слева направо, если у вас есть единственный именованный аргумент, остальные аргументы также должны быть названы.
  5. Этот вызов также не выполняется по той же причине, что и предыдущий вызов. Это удивительно? В конце концов, вы передали 4000 для аргумента с именем size , а затем «очевидно», что значение False предназначалось для аргумента a_kilobyte_is_1024_bytes .Но Python так не работает. Как только у вас есть именованный аргумент, все аргументы справа от него также должны быть именованными аргументами.

Запись читаемого кода

Я не буду утомлять вас длинной речью о том, как важно документировать ваш код. Просто знайте, что код пишется один раз, но читается много раз, и самая важная аудитория для вашего кода - это вы сами через шесть месяцев после его написания (, т.е. после того, как вы все забыли, но вам нужно что-то исправить).Python позволяет легко писать читаемый код, поэтому воспользуйтесь им. Вы меня поблагодарите через полгода.

Строки документации

Вы можете задокументировать функцию Python, указав ей строку документации ( docstring для краткости). В этой программе функция приближающегося_размера () имеет строку документации :

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):
    '' 'Преобразование размера файла в удобочитаемую форму.

    Аргументы ключевого слова:
    size - размер файла в байтах
    a_kilobyte_is_1024_bytes - если True (по умолчанию), использовать кратные 1024
                                если False, используйте число, кратное 1000

    Возвращает: строка

    ''  

Каждая функция заслуживает достойной документации.

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

☞Тройные кавычки - это также простой способ определить строку как с одинарными, так и с двойными кавычками, например qq /.../ в Perl 5.

Все, что находится между тройными кавычками, - это строка документации функции , которая документирует, что функция делает.Строка документации , если она существует, должна быть первой, что определено в функции (то есть на следующей строке после объявления функции). Технически вам не нужно давать вашей функции строку документации , но вы всегда должны это делать. Я знаю, что вы слышали это на каждом занятии по программированию, которое когда-либо посещали, но Python дает вам дополнительный стимул: строка документации доступна во время выполнения как атрибут функции.

☞Многие IDE Python используют строку документации для предоставления контекстно-зависимой документации, так что при вводе имени функции ее строка документа появляется как всплывающая подсказка.Это может быть невероятно полезно, но настолько хорошо, насколько хороша строка документации , которую вы напишете.

Импорт Путь поиска

Прежде чем это пойдет дальше, я хочу вкратце упомянуть путь поиска библиотеки. Когда вы пытаетесь импортировать модуль, Python смотрит в нескольких местах. В частности, он просматривает все каталоги, определенные в sys.path . Это просто список, и вы можете легко просмотреть или изменить его стандартными методами списка.(Вы узнаете больше о списках в Native Datatypes.)

  >>>   импортная система  >>>   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' (встроенный)> 
  >>>   систем.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']  
  1. Импорт модуля sys делает доступными все его функции и атрибуты.
  2. sys.path - это список имен каталогов, составляющих текущий путь поиска. (Ваш будет выглядеть по-разному, в зависимости от вашей операционной системы, какой версии Python вы используете и где она была изначально установлена.) Python будет просматривать эти каталоги (в этом порядке) в поисках файла .py , имя которого совпадает с что вы пытаетесь импортировать.
  3. На самом деле, я солгал; правда более сложная, потому что не все модули хранятся как .py файлов. Некоторые из них - это встроенных модулей ; они фактически встроены в сам Python. Встроенные модули ведут себя так же, как обычные модули, но их исходный код на Python недоступен, потому что они написаны не на Python! (Как и сам Python, эти встроенные модули написаны на C.)
  4. Вы можете добавить новый каталог в путь поиска Python во время выполнения, добавив имя каталога в sys.path , и тогда Python также будет искать в этом каталоге всякий раз, когда вы пытаетесь импортировать модуль.Эффект длится, пока работает Python.
  5. Используя sys.path.insert (0, new_path ) , вы вставили новый каталог в качестве первого элемента в списке sys.path и, следовательно, в начало пути поиска Python. Это почти всегда то, что вам нужно. В случае конфликтов имен (например, если Python поставляется с версией 2 определенной библиотеки, но вы хотите использовать версию 3), это гарантирует, что ваши модули будут найдены и использованы вместо модулей, поставляемых с Python.

Все является объектом

Если вы его пропустили, я только что сказал, что функции Python имеют атрибуты и что эти атрибуты доступны во время выполнения. Функция, как и все остальное в Python, является объектом.

Запустите интерактивную оболочку Python и выполните следующие действия:

  >>>   импорт человеческий  >>>   печать (humanize.approximate_size (4096, True))  4.0 КБ 
  >>>   печать (humanize.approximate_size .__ doc__)  Преобразование размера файла в удобочитаемую форму.

    Аргументы ключевого слова:
    size - размер файла в байтах
    a_kilobyte_is_1024_bytes - если True (по умолчанию), использовать кратные 1024
                                если False, используйте число, кратное 1000

    Возвращает: строка

  
  1. Первая строка импортирует программу humanize как модуль - фрагмент кода, который можно использовать в интерактивном режиме или из более крупной программы Python.После импорта модуля вы можете ссылаться на

.

Ваша первая программа на Python - погрузитесь в Python 3

Ваша первая программа на Python - погрузитесь в Python 3

Вы здесь: Домой ‣ Погрузитесь в Python 3 ‣

Уровень сложности: ♦ ♢♢♢♢

❝ Не ​​хорони свою ношу в святом молчании. У тебя проблемы? Отлично. Радуйтесь, ныряйте и исследуйте. №
- Дост. Хенепола Гунаратана

Дайвинг в

Конвенция

гласит, что я должен утомить вас фундаментальными строительными блоками программирования, чтобы мы могли медленно работать над созданием чего-то полезного.Давай пропустим все это. Вот полная работающая программа на Python. Возможно, для вас это не имеет никакого смысла. Не беспокойтесь об этом, потому что вы будете разбирать это строка за строкой. Но сначала прочтите его и посмотрите, что вы можете с этим поделать.

[загрузить humanize.py ]

  СУФФИКСОВ = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):
    '' 'Преобразование размера файла в удобочитаемую форму.Аргументы ключевого слова:
    size - размер файла в байтах
    a_kilobyte_is_1024_bytes - если True (по умолчанию), использовать кратные 1024
                                если False, используйте число, кратное 1000

    Возвращает: строка

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

    multiple = 1024, если a_kilobyte_is_1024_bytes иначе 1000
    для суффикса в СУФФИКСАХ [несколько]:
        size / = несколько
        если размер <несколько:
            вернуть '{0: .1f} {1}'. формат (размер, суффикс)

    поднять ValueError ('слишком большое число')

если __name__ == '__main__':
    print (приблизительный_размер (1000000000000; Ложь))
    печать (приблизительный_размер (1000000000000))  

Теперь давайте запустим эту программу из командной строки.В Windows это будет выглядеть примерно так:

  c: \ home \ diveintopython3 \ examples>   c: \ python31 \ python.exe humanize.py 
  1,0 ТБ
931,3 ГиБ  

В Mac OS X или Linux это будет выглядеть примерно так:

  you @ localhost: ~ / diveintopython3 / examples $   python3 humanize.py 
  1,0 ТБ
931,3 ГиБ  

Что только что произошло? Вы выполнили свою первую программу на Python. Вы вызвали интерпретатор Python в командной строке и передали имя сценария, который должен выполнить Python.Сценарий определяет единственную функцию, функцию corrective_size () , которая принимает точный размер файла в байтах и ​​вычисляет «красивый» (но приблизительный) размер. (Вы, вероятно, видели это в проводнике Windows, или в Mac OS X Finder, или в Nautilus, или Dolphin, или в Thunar в Linux. Если вы отобразите папку с документами в виде многоколоночного списка, отобразится таблица со значком документа , имя документа, размер, тип, дату последнего изменения и т. д. Если папка содержит файл размером 1093 байта с именем TODO , ваш файловый менеджер не будет отображать TODO 1093 байта ; он скажет что-то вроде TODO 1 KB вместо этого.Это то, что делает функция приближаться к размеру () .)

Посмотрите на нижнюю часть скрипта, и вы увидите два вызова print (приблизительный_размер ( аргументов )) . Это вызовы функций: сначала вызывается функция приближаться к размеру () и передается ряд аргументов, затем берется возвращаемое значение и передается прямо в функцию print () . Функция print () является встроенной; вы никогда не увидите явного объявления этого.Вы можете просто использовать его в любое время и в любом месте. (Есть много встроенных функций, и гораздо больше функций, которые разделены на модули . Терпение, кузнечик.)

Так почему же выполнение сценария в командной строке каждый раз дает один и тот же результат? Мы к этому еще вернемся. Во-первых, давайте посмотрим на функцию apply_size () .

Объявление функций

Python имеет функции, как и большинство других языков, но у него нет отдельных файлов заголовков, таких как C ++ или интерфейс , реализация /, разделы , такие как Pascal.Когда вам нужна функция, просто объявите ее, например:

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):  

Когда вам нужна функция, просто объявите ее.

Ключевое слово def запускает объявление функции, за которым следует имя функции, за которым следуют аргументы в круглых скобках. Несколько аргументов разделяются запятыми.

Также обратите внимание, что функция не определяет возвращаемый тип данных. Функции Python не указывают тип возвращаемого значения; они даже не указывают, возвращают ли они значение.(Фактически, каждая функция Python возвращает значение; если функция когда-либо выполняет оператор return , она вернет это значение, в противном случае она вернет None , нулевое значение Python.)

☞На некоторых языках функции (возвращающие значение) начинаются с , функция , а подпрограммы (которые не возвращают значение) начинаются с sub . В Python нет подпрограмм. Все является функцией, все функции возвращают значение (даже если это None ), и все функции начинаются с def .

Функция приближаться к размеру () принимает два аргумента - size и a_kilobyte_is_1024_bytes - но ни один из аргументов не определяет тип данных. В Python переменные никогда не вводятся явно. Python определяет, к какому типу относится переменная, и отслеживает ее внутренне.

☞В Java и других языках со статической типизацией необходимо указать тип данных возвращаемого значения функции и каждого аргумента функции. В Python вы никогда не указываете явно тип данных для чего-либо.В зависимости от того, какое значение вы назначаете, Python отслеживает тип данных внутри себя.

Необязательные и именованные аргументы

Python позволяет аргументам функции иметь значения по умолчанию; если функция вызывается без аргумента, аргумент получает значение по умолчанию. Кроме того, аргументы можно указывать в любом порядке с помощью именованных аргументов.

Давайте еще раз посмотрим на это объявление функции приближенно_size () :

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):  

Второй аргумент, a_kilobyte_is_1024_bytes , указывает значение по умолчанию True .Это означает, что аргумент необязательный ; вы можете вызвать функцию без него, и Python будет действовать так, как если бы вы вызвали ее с True в качестве второго параметра.

Теперь посмотрим на нижнюю часть скрипта:

  если __name__ == '__main__':
    print (приблизительный_размер (1000000000000, Ложь)) ①
    печать (приблизительный_размер (1000000000000)) ②  
  1. Это вызывает функцию приближаться к размеру () с двумя аргументами. В функции приближаться к размеру () , a_kilobyte_is_1024_bytes будет False , поскольку вы явно передали False в качестве второго аргумента.
  2. Это вызывает функцию приближаться к размеру () только с одним аргументом. Но это нормально, потому что второй аргумент не обязателен! Поскольку вызывающий не указывает, второй аргумент по умолчанию равен True , как определено в объявлении функции.

Вы также можете передавать значения в функцию по имени.

  >>>   из человеческого размера импорт приблизительно 
  >>>   приблизительный_размер (4000, a_kilobyte_is_1024_bytes = False)  '4.0 КБ '
  >>>   приблизительный_размер (размер = 4000, a_kilobyte_is_1024_bytes = False)  '4,0 КБ' 
  >>>   приблизительный_размер (a_kilobyte_is_1024_bytes = False, size = 4000)  '4,0 КБ' 
  >>>   приблизительный_размер (a_kilobyte_is_1024_bytes = False, 4000)  Файл "", строка 1
SyntaxError: аргумент без ключевого слова после аргумента ключевого слова 
  >>>   приблизительный_размер (размер = 4000, False)  Файл "", строка 1
SyntaxError: аргумент без ключевого слова после аргумента ключевого слова  
  1. Это вызывает функцию приближаться к размеру () с 4000 для первого аргумента ( размер ) и False для аргумента с именем a_kilobyte_is_1024_bytes .(Это второй аргумент, но он не имеет значения, как вы увидите через минуту.)
  2. Это вызывает функцию приближаться к размеру () с 4000 для аргумента с именем size и False для аргумента с именем a_kilobyte_is_1024_bytes . (Эти именованные аргументы находятся в том же порядке, что и аргументы, перечисленные в объявлении функции, но это тоже не имеет значения.)
  3. Это вызывает функцию приближаться к размеру () с False для аргумента с именем a_kilobyte_is_1024_bytes и 4000 для аргумента с именем размер .(Видите? Я сказал вам, что порядок не имеет значения.)
  4. Этот вызов завершается неудачно, потому что у вас есть именованный аргумент, за которым следует безымянный (позиционный) аргумент, и это никогда не работает. При чтении списка аргументов слева направо, если у вас есть единственный именованный аргумент, остальные аргументы также должны быть названы.
  5. Этот вызов тоже не выполняется по той же причине, что и предыдущий. Это удивительно? В конце концов, вы передали 4000 для аргумента с именем size , а затем «очевидно», что значение False предназначалось для аргумента a_kilobyte_is_1024_bytes .Но Python так не работает. Как только у вас есть именованный аргумент, все аргументы справа от него также должны быть именованными аргументами.

Запись читаемого кода

Я не буду утомлять вас длинной речью о том, как важно документировать ваш код. Просто знайте, что код пишется один раз, но читается много раз, и самая важная аудитория для вашего кода - это вы сами через шесть месяцев после его написания (, т.е. после того, как вы все забыли, но вам нужно что-то исправить).Python позволяет легко писать читаемый код, поэтому воспользуйтесь им. Вы меня поблагодарите через полгода.

Строки документации

Вы можете задокументировать функцию Python, указав ей строку документации ( docstring для краткости). В этой программе функция приближенно_size () имеет строку документации :

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True):
    '' 'Преобразование размера файла в удобочитаемую форму.

    Аргументы ключевого слова:
    size - размер файла в байтах
    a_kilobyte_is_1024_bytes - если True (по умолчанию), использовать кратные 1024
                                если False, используйте число, кратное 1000

    Возвращает: строка

    ''  

Каждая функция заслуживает достойной документации.

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

☞Тройные кавычки - это также простой способ определить строку как с одинарными, так и с двойными кавычками, например qq /.../ в Perl 5.

Все, что находится между тройными кавычками, - это строка документации функции , которая документирует, что функция делает.Строка документации , если она существует, должна быть первой, что определено в функции (то есть на следующей строке после объявления функции). Технически вам не нужно давать вашей функции строку документации , но вы всегда должны это делать. Я знаю, что вы слышали это на каждом занятии по программированию, которое когда-либо посещали, но Python дает вам дополнительный стимул: строка документации доступна во время выполнения как атрибут функции.

☞Многие IDE Python используют строку документации для предоставления контекстно-зависимой документации, так что при вводе имени функции ее строка документа появляется как всплывающая подсказка.Это может быть невероятно полезно, но настолько хорошо, насколько хороша строка документации , которую вы напишете.

Импорт Путь поиска

Прежде чем это пойдет дальше, я хочу вкратце упомянуть путь поиска библиотеки. Когда вы пытаетесь импортировать модуль, Python смотрит в нескольких местах. В частности, он просматривает все каталоги, определенные в sys.path . Это просто список, и вы можете легко просмотреть или изменить его стандартными методами списка.(Вы узнаете больше о списках в Native Datatypes.)

  >>>   импортная система  >>>   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' (встроенный)> 
  >>>   систем.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']  
  1. Импорт модуля sys делает доступными все его функции и атрибуты.
  2. sys.path - это список имен каталогов, составляющих текущий путь поиска. (Ваш будет выглядеть по-разному, в зависимости от вашей операционной системы, какой версии Python вы используете и где она была изначально установлена.) Python будет просматривать эти каталоги (в этом порядке) в поисках файла .py , имя которого совпадает с что вы пытаетесь импортировать.
  3. На самом деле, я солгал; правда более сложная, потому что не все модули хранятся как .py файлов. Некоторые из них - это встроенных модулей ; они фактически встроены в сам Python. Встроенные модули ведут себя так же, как обычные модули, но их исходный код на Python недоступен, потому что они написаны не на Python! (Как и сам Python, эти встроенные модули написаны на C.)
  4. Вы можете добавить новый каталог в путь поиска Python во время выполнения, добавив имя каталога в sys.path , и тогда Python также будет искать в этом каталоге всякий раз, когда вы пытаетесь импортировать модуль.Эффект длится, пока работает Python.
  5. Используя sys.path.insert (0, new_path ) , вы вставили новый каталог в качестве первого элемента в списке sys.path и, следовательно, в начало пути поиска Python. Это почти всегда то, что вам нужно. В случае конфликтов имен (например, если Python поставляется с версией 2 определенной библиотеки, но вы хотите использовать версию 3), это гарантирует, что ваши модули будут найдены и использованы вместо модулей, поставляемых с Python.

Все является объектом

Если вы его пропустили, я только что сказал, что функции Python имеют атрибуты и что эти атрибуты доступны во время выполнения. Функция, как и все остальное в Python, является объектом.

Запустите интерактивную оболочку Python и следуйте:

  >>>   импорт человеческий  >>>   печать (humanize.approximate_size (4096, True))  4.0 КБ 
  >>>   печать (humanize.approximate_size .__ doc__)  Преобразование размера файла в удобочитаемую форму.

    Аргументы ключевого слова:
    size - размер файла в байтах
    a_kilobyte_is_1024_bytes - если True (по умолчанию), использовать кратные 1024
                                если False, используйте число, кратное 1000

    Возвращает: строка

  
  1. Первая строка импортирует программу humanize как модуль - фрагмент кода, который можно использовать в интерактивном режиме или из более крупной программы Python.После импорта модуля вы можете ссылаться на любые его общедоступные функции, классы или атрибуты. Модули могут делать это для доступа к функциям в других модулях, и вы также можете сделать это в интерактивной оболочке Python. Это важная концепция, и вы увидите ее гораздо больше в этой книге.
  2. Если вы хотите использовать функции, определенные в импортированных модулях, вам необходимо указать имя модуля. Таким образом, вы не можете просто сказать приблизительный_размер ; он должен быть человеческий размер. приблизительный_размер .Если вы использовали классы в Java, это должно показаться смутно знакомым.
  3. Вместо того, чтобы вызывать функцию, как вы ожидали, вы запросили один из атрибутов функции, __doc__ .

import в Python - это как требует в Perl. Как только вы импортируете модуль Python, вы получаете доступ к его функциям с помощью модуля . функция ; как только вам потребуется модуль Perl, вы получите доступ к его функциям с помощью модуля :: функции .

Что такое объект?

Все в Python является объектом, и все может иметь атрибуты и методы. Все функции имеют встроенный атрибут __doc__ , который возвращает строку документации , определенную в исходном коде функции. Модуль sys - это объект, который имеет (среди прочего) атрибут под названием path . И так далее.

Тем не менее, это не дает ответа на более фундаментальный вопрос: что такое объект? В разных языках программирования «объект» определяется по-разному.В некоторых случаях это означает, что все объекта должны иметь атрибуты и методы; в других случаях это означает, что все объекты являются подклассами. В Python определение более расплывчатое. Некоторые объекты не имеют ни атрибутов, ни методов, , но могут . Не все объекты подклассифицируются. Но все является объектом в том смысле, что его можно присвоить переменной или передать в качестве аргумента функции.

Возможно, вы слышали термин «первоклассный объект» в других контекстах программирования.В Python функции - это первоклассных объектов . Вы можете передать функцию в качестве аргумента другой функции. Модули - это первоклассных объектов . Вы можете передать весь модуль в качестве аргумента функции. Классы являются объектами первого класса, а отдельные экземпляры класса также являются объектами первого класса.

Это важно, поэтому я повторю это, если вы пропустили первые несколько раз: все в Python является объектом . Строки - это объекты. Списки - это объекты.Функции - это объекты. Классы - это объекты. Экземпляры классов - это объекты. Даже модули - это объекты.

Код отступа

Функции Python не имеют явного begin или end , а также фигурных скобок, отмечающих начало и конец кода функции. Единственный разделитель - это двоеточие (: ) и отступ самого кода.

  def приблизительный_размер (размер, a_kilobyte_is_1024_bytes = True): ①
    если размер <0: ②
        поднять ValueError ('число должно быть неотрицательным') ③
                                                            ④
    multiple = 1024, если a_kilobyte_is_1024_bytes иначе 1000
    для суффикса в СУФФИКСАХ [несколько]: ⑤
        size / = несколько
        если размер <несколько:
            вернуть '{0 :.1f} {1} '. Формат (размер, суффикс)

    поднять ValueError ("слишком большое число")  
  1. Кодовые блоки определяются их отступом. Под «блоком кода» я имею в виду функции, , если операторы , для циклов , при циклах и т. Д. Отступ начинается с блока, а снятие отступа заканчивает его. Нет явных скобок, скобок или ключевых слов. Это означает, что пробелы имеют большое значение и должны быть согласованными. В этом примере код функции имеет отступ в четыре пробела.Это не обязательно должно быть четыре пробела, оно просто должно быть последовательным. Первая строка без отступа отмечает конец функции.
  2. В Python за оператором if следует блок кода. Если выражение if истинно, выполняется блок с отступом, в противном случае он попадает в блок else (если есть). Обратите внимание на отсутствие скобок вокруг выражения.
  3. Эта строка находится внутри блока кода if . Этот оператор raise вызовет исключение (типа ValueError ), но только если размер <0 .
  4. Это , а не конец функции. Полностью пустые строки не учитываются. Они могут сделать код более читабельным, но не считаются разделителями блоков кода. Функция продолжается на следующей строке.
  5. Цикл для также отмечает начало кодового блока. Блоки кода могут содержать несколько строк, если все они имеют одинаковый отступ. Этот цикл для содержит три строки кода. Другого специального синтаксиса для многострочных кодовых блоков нет.Просто сделайте отступ и продолжайте жить своей жизнью.

После некоторых первоначальных протестов и нескольких ехидных аналогий с Фортраном вы примиритесь с этим и начнете видеть его преимущества. Одним из основных преимуществ является то, что все программы Python выглядят одинаково, поскольку отступы - это требование языка, а не вопрос стиля. Это упрощает чтение и понимание кода Python других людей.

☞Python использует возврат каретки для разделения операторов и двоеточие и отступы для разделения блоков кода. C ++ и Java используют точки с запятой для разделения операторов и фигурные скобки для разделения блоков кода.

Исключения

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

Что такое исключение? Обычно это ошибка, признак того, что что-то пошло не так.(Не все исключения являются ошибками, но пока это не важно.) Некоторые языки программирования поощряют использование кодов возврата ошибок, которые вы проверяете . Python поощряет использование исключений, которые вы обрабатываете .

Когда в Python Shell возникает ошибка, она распечатывает некоторые подробности об исключении и о том, как это произошло, и все. Это называется необработанным исключением . Когда возникло исключение, не было кода, который бы его явно заметил и обработал, поэтому он вернулся на верхний уровень оболочки Python, который выдает некоторую отладочную информацию и прекращает работу.В оболочке это не имеет большого значения, но если это произойдет во время работы вашей реальной программы Python, вся программа резко остановится, если ничто не обработает исключение. Может быть, вы этого хотите, а может, и нет.

☞В отличие от Java, функции Python не объявляют, какие исключения они могут вызвать. Вам решать, какие исключения вам нужно перехватывать.

Однако исключение не обязательно должно приводить к полному сбою программы.Исключения могут быть обработаны . Иногда исключение действительно возникает из-за ошибки в коде (например, при доступе к несуществующей переменной), но иногда исключение - это то, что вы можете ожидать. Если вы открываете файл, он может не существовать. Если вы импортируете модуль, возможно, он не установлен. Если вы подключаетесь к базе данных, она может быть недоступна или у вас могут отсутствовать правильные учетные данные для доступа к ней. Если вы знаете, что строка кода может вызвать исключение, вам следует обработать исключение с помощью попытки ... кроме блока .

☞Python использует try ... кроме блоков для обработки исключений и оператора raise для их генерации. Java и C ++ используют блоки try ... catch для обработки исключений и оператор throw для их генерации.

Функция приближенно_size () вызывает исключения в двух разных случаях: если заданный размер , больше, чем функция предназначена для обработки, или если он меньше нуля.

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

Синтаксис вызова исключения достаточно прост. Используйте оператор raise , за которым следует имя исключения и необязательная удобочитаемая строка для целей отладки. Синтаксис напоминает вызов функции. (На самом деле исключения реализованы как классы, и этот оператор raise фактически создает экземпляр класса ValueError и передает строку 'number must be non-negative' его методу инициализации.Но мы забегаем вперед!)

: Вам не нужно обрабатывать исключение в функции, которая его вызывает. Если одна функция не справляется с этим, исключение передается вызывающей функции, затем вызывающей функции этой функции и так далее «вверх по стеку». Если исключение никогда не обрабатывается, ваша программа выйдет из строя, Python напечатает «трассировку» до стандартной ошибки, и это конец. Опять же, может быть, это то, что вы хотите; это зависит от того, что делает ваша программа.

Перехват ошибок импорта

Одно из встроенных исключений Python - ImportError , которое возникает, когда вы пытаетесь импортировать модуль и терпите неудачу.Это может произойти по разным причинам, но самый простой случай - когда модуль не существует в вашем пути поиска импорта. Вы можете использовать это для включения дополнительных функций в вашу программу. Например, библиотека chardet обеспечивает автоматическое определение кодировки символов. Возможно, ваша программа хочет использовать эту библиотеку , если она существует, , но продолжайте изящно, если пользователь ее не установил. Вы можете сделать это с помощью блока try..except .

   попробуйте :
  импортная диаграмма
 , кроме  ImportError:
  chardet = Нет  

Позже вы можете проверить наличие модуля chardet с помощью простого оператора if :

  если chardet:
  # сделай что-нибудь
еще:
  # все равно продолжить  

Другое распространенное использование исключения ImportError - это когда два модуля реализуют общий API , но один более желателен, чем другой.(Может быть, он быстрее или использует меньше памяти.) Вы можете попробовать импортировать один модуль, но вернуться к другому модулю, если первый импорт завершился неудачно. Например, в главе XML рассказывается о двух модулях, реализующих общий API , который называется ElementTree API . Первый, lxml , является сторонним модулем, который вам нужно скачать и установить самостоятельно. Второй, xml.etree.ElementTree , работает медленнее, но является частью стандартной библиотеки Python 3.

  попробовать:
    из lxml import etree
кроме ImportError:
    импортировать xml.etree.ElementTree как etree  

К концу этой попытки .. кроме блока вы импортировали какой-то модуль и назвали его etree . Поскольку оба модуля реализуют общий API , остальной части вашего кода не нужно постоянно проверять, какой модуль был импортирован. А поскольку модуль, который импортировал , всегда называется etree , остальная часть вашего кода не должна быть засорена операторами if для вызова модулей с разными именами.

Несвязанные переменные

Взгляните еще раз на эту строку кода из функции приближенно_size () :

  несколько = 1024, если a_kilobyte_is_1024_bytes иначе 1000  

Вы никогда не объявляете переменную кратной , вы просто присваиваете ей значение. Это нормально, потому что Python позволяет это делать. Что Python не позволит вам сделать, так это ссылаться на переменную, которой никогда не было присвоено значение. Попытка сделать это вызовет исключение NameError .

  >>>   x 
  Traceback (последний звонок последний):
  Файл "", строка 1, в 
NameError: имя 'x' не определено 
  >>>   x = 1 
  >>>   x 
  1  

Однажды вы поблагодарите Python за это.

Все с учетом регистра

Все имена в Python чувствительны к регистру: имена переменных, имена функций, имена классов, имена модулей, имена исключений.Если вы можете его получить, установить, вызвать, построить, импортировать или поднять, он чувствителен к регистру.

  >>>   an_integer = 1 
  >>>   an_integer 
  1 
  >>>   AN_INTEGER 
  Traceback (последний звонок последний):
  Файл "", строка 1, в 
NameError: имя AN_INTEGER не определено 
  >>>   An_Integer 
  Traceback (последний звонок последний):
  Файл "", строка 1, в 
NameError: имя An_Integer не определено 
  >>>   ан_интегр 
  Traceback (последний звонок последний):
  Файл "", строка 1, в 
NameError: имя an_inteGer не определено 
 

И так далее.

Запуск скриптов

Все в Python - это объект.
Модули

Python являются объектами и имеют несколько полезных атрибутов. Вы можете использовать это, чтобы легко тестировать свои модули по мере их написания, включив специальный блок кода, который выполняется, когда вы запускаете файл Python в командной строке. Возьмите последние несколько строк файла humanize.py :

.

 
если __name__ == '__main__':
    print (приблизительный_размер (1000000000000; Ложь))
    печать (приблизительный_размер (1000000000000))  

☞ Подобно C , Python использует == для сравнения и = для присвоения.В отличие от C , Python не поддерживает встроенное назначение, поэтому нет никаких шансов случайно присвоить значение, которое, как вы думали, сравниваете.

Так что же делает этот оператор if особенным? Что ж, модули - это объекты, и все модули имеют встроенный атрибут __name__ . __name__ модуля зависит от того, как вы его используете. Если вы импортируете модуль , тогда __name__ - это имя файла модуля без пути к каталогу или расширения файла.

  >>>   импорт человеческий 
  >>>   человеческий размер .__ имя__ 
  "человеческий размер"  

Но вы также можете запустить модуль напрямую как отдельную программу, и в этом случае __name__ будет специальным значением по умолчанию, __main__ . Python оценит этот оператор if , найдет истинное выражение и выполнит блок кода if . В этом случае для вывода двух значений.

  c: \ home \ diveintopython3>   c: \ python31 \ python.exe peopleize.py 
  1,0 ТБ
931,3 ГиБ  

И это ваша первая программа на Python!

Дополнительная литература

  • PEP 257: Docstring Conventions объясняет, что отличает хорошую docstring от отличной docstring .
  • Python Tutorial: Documentation Strings также затрагивает эту тему.
  • PEP 8: Style Guide for Python Code обсуждает хороший стиль отступов.
  • Справочное руководство по Python объясняет, что значит сказать, что все в Python является объектом, потому что некоторые люди педанты и любят подробно обсуждать подобные вещи.

☜ ☞

© 2001–11 Марк Пилигрим

.

Содержание - Погружение в Python 3

Вы здесь: Главная ‣ Погрузитесь в Python 3 ‣

  1. Что нового в «Dive Into Python 3»
    1. он же «минус уровень»
  2. Установка Python
    1. Diving In
    2. Какой Python вам подходит?
    3. Установка в Microsoft Windows
    4. Установка в Mac OS X
    5. Установка в Ubuntu Linux
    6. Установка на другие платформы
    7. Использование оболочки Python
    8. Редакторы Python и IDE
  3. Ваша первая программа на Python
    1. Дайвинг в
    2. Объявление функций
      1. Необязательные и именованные аргументы
    3. Запись читаемого кода
      1. Строки документации
    4. Импорт Путь поиска
    5. Все является объектом
      1. Что такое объект?
    6. Код отступа
    7. Исключения
      1. Перехват ошибок импорта
    8. Несвязанные переменные
    9. Все с учетом регистра
    10. Запуск скриптов
    11. Дополнительная литература
  4. Собственные типы данных
    1. Дайвинг в
    2. Логические значения
    3. Номера
        Приведение

      1. целых чисел к плавающим и наоборот
      2. Общие числовые операции
      3. Дроби
      4. Тригонометрия
      5. Числа в логическом контексте
    4. Списки
      1. Создание списка
      2. Нарезка списка
      3. Добавление элементов в список
      4. Поиск значений в списке
      5. Удаление элементов из списка
      6. Удаление элементов из списка: бонусный раунд
      7. Списки в логическом контексте
    5. Кортежи
      1. кортежей в логическом контексте
      2. Одновременное присвоение нескольких значений
    6. Наборы
      1. Создание набора
      2. Изменение набора
      3. Удаление элементов из набора
      4. Общие операции над наборами
      5. Множества в логическом контексте
    7. Словари
      1. Создание словаря
      2. Изменение словаря
      3. Разнозначные словари
      4. Словари в логическом контексте
    8. Нет
      1. Нет В логическом контексте
    9. Дополнительная литература
  5. Понимания
    1. Дайвинг в
    2. Работа с файлами и каталогами
      1. Текущий рабочий каталог
      2. Работа с именами файлов и каталогов
      3. Листинговые каталоги
      4. Получение метаданных файла
      5. Построение абсолютных путей
    3. Составление списка
    4. Словарь терминов
      1. Другие забавные вещи, связанные с пониманием словаря
    5. Набор понятий
    6. Дополнительная литература
  6. Струны
    1. Некоторые скучные вещи, которые вам нужно понять, прежде чем вы сможете погрузиться в
    2. Юникод
    3. Diving In
    4. Форматирование строк
      1. Имена составных полей
      2. Спецификаторы формата
    5. Другие распространенные строковые методы
      1. Нарезка строки
    6. Строки vs.Байтов
    7. Постскриптум: кодировка символов исходного кода Python
    8. Дополнительная литература
  7. Регулярные выражения
    1. Дайвинг в
    2. Пример использования: Уличные адреса
    3. Пример использования: римские цифры
      1. Проверка тысяч
      2. Проверка сотен
    4. Использование синтаксиса {n, m}
      1. Проверка десятков и единиц
    5. Подробные регулярные выражения
    6. Пример: анализ телефонных номеров
    7. Сводка
  8. Затворы Генераторы и
    1. Дайвинг в
    2. Я знаю, давайте использовать регулярные выражения!
    3. Список функций
    4. Список шаблонов
    5. Файл шаблонов
    6. Генераторы
      1. Генератор Фибоначчи
      2. Генератор множественных правил
    7. Дополнительная литература
  9. Классы и Итераторы
    1. Дайвинг в
    2. Определение классов
      1. Метод __init __ ()
    3. Создание экземпляров классов
    4. Переменные экземпляра
    5. Итератор Фибоначчи
    6. Итератор множественных правил
    7. Дополнительная литература
  10. Расширенные итераторы
    1. Дайвинг в
    2. Поиск всех вхождений шаблона
    3. Поиск уникальных предметов в последовательности
    4. Создание утверждений
    5. Генератор выражений
    6. Вычисление перестановок… Ленивый путь!
    7. Другие забавные вещи в модуле itertools
    8. Новый вид обработки строк
    9. Оценка произвольных строк как выражений Python
    10. Собираем все вместе
    11. Дополнительная литература
  11. Модульное тестирование
    1. (Не) Дайвинг в
    2. Один вопрос
    3. «Стой и загорелся»
    4. Больше остановки, больше огня
    5. И еще кое-что…
    6. Приятная симметрия
    7. Более плохой ввод
  12. Рефакторинг
    1. Дайвинг в
    2. Обращение с изменяющимися требованиями
    3. Рефакторинг
    4. Сводка
  13. Файлы
    1. Дайвинг в
    2. Чтение из текстовых файлов
      1. Кодировка символов поднимает свою уродливую голову
      2. Объекты потока
      3. Чтение данных из текстового файла
      4. Закрытие файлов
      5. Автоматическое закрытие файлов
      6. Чтение данных по одной строке за раз
    3. Запись в текстовые файлы
      1. Снова кодировка символов
    4. Двоичные файлы
    5. Объекты потока из нефайловых источников
      1. Обработка сжатых файлов
    6. Стандартный ввод, вывод и ошибка
      1. Перенаправление стандартного вывода
    7. Дополнительная литература
  14. XML
    1. Дайвинг в
    2. 5-минутный ускоренный курс по XML
    3. Структура подачи атома
    4. Разбор XML
      1. Элементы - это списки
      2. Атрибуты - это словари
    5. Поиск узлов в XML-документе
    6. Дальше с lxml
    7. Создание XML
    8. Разбор сломанного XML
    9. Дополнительная литература
  15. Сериализация объектов Python
    1. Дайвинг в
      1. Краткое примечание о примерах в этой главе
    2. Сохранение данных в файл рассола
    3. Загрузка данных из файла рассола
    4. Травление без напильника
    5. Байт и строки снова поднимают их уродливые головы
    6. Отладка файлов рассола
    7. Сериализация объектов Python для чтения на других языках
    8. Сохранение данных в файл JSON
    9. Сопоставление типов данных Python с JSON
    10. Сериализация типов данных, не поддерживаемых JSON
    11. Загрузка данных из файла JSON
    12. Дополнительная литература
  16. Веб-службы HTTP
    1. Дайвинг в
    2. Особенности HTTP
      1. Кэширование
      2. Проверка последнего изменения
      3. Проверка ETag
      4. Сжатие
      5. Перенаправления
    3. Как не получать данные через HTTP
    4. Что в сети?
    5. Представляем httplib2
      1. Краткое отступление, чтобы объяснить, почему httplib2 возвращает байты вместо строк
      2. Как httplib2 обрабатывает кеширование
      3. Как httplib2 обрабатывает Last-Modified и ETag заголовки
      4. Как http2lib обрабатывает сжатие
      5. Как httplib2 обрабатывает перенаправления
    6. Помимо HTTP GET
    7. Помимо HTTP POST
    8. Дополнительная литература
  17. Пример использования: перенос chardet на Python 3
    1. Дайвинг в
    2. Что такое автоматическое определение кодировки символов?
      1. Разве это невозможно?
      2. Существует ли такой алгоритм?
    3. Представляем модуль chardet
      1. UTF-n с спецификацией
      2. Экранированные кодировки
      3. Многобайтовые кодировки
      4. Однобайтовые кодировки
      5. окна-1252
    4. Работает 2to3
    5. Краткий экскурс в многофайловые модули
    6. Устранение того, чего не может 2to3
      1. Ложь неверный синтаксис
      2. Нет модуля с именем константы
      3. Имя 'файл' не определено
      4. Невозможно использовать шаблон строки для байтового объекта
      5. Невозможно неявно преобразовать объект 'bytes' в str
      6. Неподдерживаемые типы операндов для +: 'int' и 'bytes'
      7. ord () ожидаемая строка длины 1, но найдено int
      8. Неупорядочиваемые типы: int () > = str ()
      9. Глобальное имя «уменьшить» не определено
    7. Резюме
  18. Упаковка библиотек Python
    1. Дайвинг в
    2. Вещи, которые Distutils не может сделать для вас
    3. Структура каталогов
    4. Написание сценария установки
    5. Классификация вашей посылки
      1. Примеры хороших классификаторов упаковки
    6. Указание дополнительных файлов с помощью манифеста
    7. Проверка сценария установки на наличие ошибок
    8. Создание дистрибутива исходного кода
    9. Создание графического установщика
      1. Сборка устанавливаемых пакетов для других операционных систем
    10. Добавление программного обеспечения в указатель пакетов Python
    11. Множество возможных вариантов упаковки Python
    12. Дополнительная литература
  19. Перенос кода на Python 3 с 2to3
    1. Дайвинг в
    2. распечатать выписка
    3. строковые литералы Unicode
    4. unicode () глобальная функция
    5. длинный тип данных
    6. <> сравнение
    7. has_key () метод словаря
    8. Методы словаря, возвращающие списки
    9. Модули, которые были переименованы или реорганизованы
      1. http
      2. urllib
      3. дБм
      4. xmlrpc
      5. Другие модули
    10. Относительный импорт в пакете
    11. next () метод итератора
    12. фильтр () глобальная функция
    13. карта () глобальная функция
    14. уменьшить () глобальная функция
    15. apply () глобальная функция
    16. стажер () глобальная функция
    17. инструкция exec
    18. execfile инструкция
    19. repr литералов (обратные кавычки)
    20. попробовать... кроме заявления
    21. заявление о подъёме
    22. бросить метод на генераторы
    23. xrange () глобальная функция
    24. raw_input () и input () глобальные функции
    25. func_ * атрибуты функции
    26. xreadlines () Метод ввода-вывода
    27. лямбда функции, которые принимают кортеж вместо нескольких параметров
    28. Атрибуты специального метода
    29. __nonzero__ специальный метод
    30. Восьмеричные литералы
    31. систем.максинт
    32. callable () глобальная функция
    33. zip () глобальная функция
    34. StandardError исключение
    35. типы константы модуля
    36. isinstance () глобальная функция
    37. basestring тип данных
    38. itertools модуль
    39. sys.exc_type , sys.exc_value , sys.exc_traceback
    40. Составление списков над кортежами
    41. ос.getcwdu () функция
    42. Метаклассы
    43. Вопросы стиля
      1. set () литералов (явный)
      2. buffer () глобальная функция (явная)
      3. Пробелы вокруг запятых (явно)
      4. Общие идиомы (явный)
  20. Имена специальных методов
    1. Дайвинг в
    2. Основы
    3. Классы, которые действуют как итераторы
    4. Вычисляемые атрибуты
    5. Классы, которые действуют как функции
    6. Классы, которые действуют как наборы
    7. Классы, которые действуют как словари
    8. Классы, которые действуют как числа
    9. Классы, которые можно сравнивать
    10. Классы, которые можно сериализовать
    11. Классы, которые могут использоваться в с блоком
    12. Действительно эзотерические вещи
    13. Дополнительная литература
  21. Куда идти дальше
    1. Что нужно прочитать
    2. Где искать код, совместимый с Python 3
  22. Поиск и устранение неисправностей
    1. Дайвинг в
    2. Доступ к командной строке
    3. Запуск Python из командной строки

© 2001–11 Марк Пилигрим

.

понятий - погрузиться в Python 3

понимания - погрузиться в Python 3

Вы здесь: Домой ‣ Погрузитесь в Python 3 ‣

Уровень сложности: ♦♦ ♢♢♢

❝ Наше воображение растянуто до предела, не для того, чтобы, как в художественной литературе, представить вещи, которых на самом деле нет, а просто для того, чтобы постичь те вещи, которые есть. ❞
- Ричард Фейнман

Дайвинг в

Каждый язык программирования имеет одну особенность - сложную вещь, намеренно упрощенную.Если вы переходите с другого языка, вы могли легко его пропустить, потому что ваш старый язык не делал эту вещь простой (потому что вместо этого он был занят чем-то другим простым). В этой главе вы узнаете о составлении списков, словарях и множествах: трех связанных концепциях, сосредоточенных вокруг одной очень мощной техники. Но сначала я хочу сделать небольшой обход двух модулей, которые помогут вам ориентироваться в вашей локальной файловой системе.

Работа с файлами и каталогами

Python 3 поставляется с модулем os , что означает «операционная система.Модуль os содержит множество функций для получения информации, а в некоторых случаях и для управления локальными каталогами, файлами, процессами и переменными среды. Python делает все возможное, чтобы предложить единый API для всех поддерживаемых операционных систем, чтобы ваши программы могли работать на любом компьютере с минимально возможным кодом для конкретной платформы.

Текущий рабочий каталог

Когда вы только начинаете работать с Python, вы собираетесь проводить много времени в Python Shell.В этой книге вы встретите такие примеры:

  1. Импортируйте один из модулей в папку examples
  2. Вызов функции в этом модуле
  3. Объясните результат

Всегда есть текущий рабочий каталог.

Если вы не знаете текущий рабочий каталог, шаг 1, вероятно, завершится ошибкой ImportError . Почему? Потому что Python будет искать модуль example в пути поиска импорта, но не найдет его, потому что папка examples не является одним из каталогов в пути поиска.Чтобы обойти это, вы можете сделать одно из двух:

  1. Добавьте папку examples в путь поиска импорта
  2. Измените текущий рабочий каталог на папку примеров

Текущий рабочий каталог - это невидимое свойство, которое Python постоянно хранит в памяти. Всегда существует текущий рабочий каталог, независимо от того, находитесь ли вы в оболочке Python, выполняете ли вы собственный сценарий Python из командной строки или запускаете сценарий Python CGI где-нибудь на веб-сервере.

Модуль os содержит две функции для работы с текущим рабочим каталогом.

  >>>   импорт ОС  >>>   печать (os.getcwd ())  C: \ Python31 
  >>>   os.chdir ('/ Users / pilgrim / diveintopython3 / examples')  >>>   печать (os.getcwd ())  C: \ Users \ pilgrim \ diveintopython3 \ examples  
  1. Модуль os поставляется с Python; вы можете импортировать его в любое время и в любом месте.
  2. Используйте функцию os.getcwd () , чтобы получить текущий рабочий каталог. Когда вы запускаете графическую оболочку Python, текущий рабочий каталог запускается как каталог, в котором находится исполняемый файл Python Shell. В Windows это зависит от того, где вы установили Python; каталог по умолчанию - c: \ Python31 . Если вы запускаете оболочку Python из командной строки, текущий рабочий каталог запускается как каталог, в котором вы были при запуске python3 .
  3. Используйте os.chdir () для изменения текущего рабочего каталога.
  4. Когда я вызвал функцию os.chdir () , я использовал путь в стиле Linux (косая черта, без буквы диска), хотя я работаю в Windows. Это одно из мест, где Python пытается скрыть различия между операционными системами.

Работа с именами файлов и каталогов

Раз уж мы говорим о каталогах, я хочу отметить модуль os.path . os.path содержит функции для управления именами файлов и именами каталогов.

  >>>   импорт ОС 
  >>>   print (os.path.join ('/ Users / pilgrim / diveintopython3 / examples /', 'humanize.py'))  /Users/pilgrim/diveintopython3/examples/humansize.py 
  >>>   print (os.path.join ('/ Users / pilgrim / diveintopython3 / examples', 'humanize.py'))  / Пользователи / паломник / diveintopython3 / examples \ humanize.py 
  >>>   печать (os.path.expanduser ('~'))  c: \ Пользователи \ пилигрим 
  >>>   print (os.path.join (os.path.expanduser ('~'), 'diveintopython3', 'examples', 'humanize.py'))  c: \ Users \ pilgrim \ diveintopython3 \ examples \ humanize.py  
  1. Функция os.path.join () создает имя пути из одного или нескольких частичных имен путей. В этом случае он просто объединяет строки.
  2. В этом чуть менее тривиальном случае вызов os.Функция path.join () добавит дополнительную косую черту к имени пути перед присоединением его к имени файла. Это обратная косая черта вместо прямой, потому что я создал этот пример в Windows. Если вы воспроизведете этот пример в Linux или Mac OS X, вместо этого вы увидите косую черту. Не беспокойтесь о косых чертах; всегда используйте os.path.join () и позволяйте Python делать правильные вещи.
  3. Функция os.path.expanduser () расширит путь, который использует ~ для представления домашнего каталога текущего пользователя.Это работает на любой платформе, где у пользователей есть домашний каталог, включая Linux, Mac OS X и Windows. Возвращенный путь не имеет косой черты в конце, но функция os.path.join () не возражает.
  4. Комбинируя эти методы, вы можете легко создавать пути к каталогам и файлам в домашнем каталоге пользователя. Функция os.path.join () может принимать любое количество аргументов. Я был вне себя от радости, когда обнаружил это, поскольку addSlashIfNeeded () - одна из дурацких маленьких функций, которые мне всегда нужно писать при создании своего набора инструментов на новом языке. Не пишите эту глупую маленькую функцию на Python; умные люди уже позаботились об этом за вас.

os.path также содержит функции для разделения полных путей, имен каталогов и имен файлов на их составные части.

  >>>   путь = '/Users/pilgrim/diveintopython3/examples/humansize.py' 
  >>>   os.path.split (путь)  ('/ Users / pilgrim / diveintopython3 / examples', 'humanize.py ') 
  >>>   (имя каталога, имя файла) = os.path.split (путь)  >>>   имя каталога  '/ Пользователи / pilgrim / diveintopython3 / examples' 
  >>>   имя файла  "humanize.py" 
  >>>   (короткое имя, расширение) = os.path.splitext (имя файла)  >>>   короткое имя 
  "человеческий размер" 
  >>>   расширение 
  '.ру ' 
  1. Функция split разбивает полный путь и возвращает кортеж, содержащий путь и имя файла.
  2. Помните, я сказал, что вы можете использовать присвоение нескольких переменных для возврата нескольких значений из функции? Именно это и делает функция os.path.split () . Вы присваиваете возвращаемое значение функции split кортежу из двух переменных. Каждая переменная получает значение соответствующего элемента возвращаемого кортежа.
  3. Первая переменная, dirname , получает значение первого элемента кортежа, возвращенного функцией os.path.split () , путь к файлу.
  4. Вторая переменная, filename , получает значение второго элемента кортежа, возвращенного функцией os.path.split () , имя файла.
  5. os.path также содержит функцию os.path.splitext () , которая разделяет имя файла и возвращает кортеж, содержащий имя файла и расширение файла.Вы используете ту же технику, чтобы назначить каждую из них отдельным переменным.

Листинговые каталоги

Модуль glob - еще один инструмент в стандартной библиотеке Python. Это простой способ получить содержимое каталога программно, и в нем используются символы подстановки, с которыми вы, возможно, уже знакомы по работе в командной строке.

Модуль glob использует подстановочные знаки, подобные оболочке.

  >>>   ос.chdir ('/ Пользователи / pilgrim / diveintopython3 /') 
  >>>   импортный глоб 
  >>>   glob.glob ('examples / *. Xml')  ['examples \\ feed-broken.xml',
 'examples \\ feed-ns0.xml',
 'examples \\ feed.xml'] 
  >>>   os.chdir ('examples /')  >>>   glob.glob ('* test * .py')  ['alphameticstest.py',
 'pluraltest1.py',
 'pluraltest2.py',
 'pluraltest3.py',
 'pluraltest4.py',
 'множественное число5.ру ',
 'pluraltest6.py',
 'romantest1.py',
 'romantest10.py',
 'romantest2.py',
 'romantest3.py',
 'romantest4.py',
 'romantest5.py',
 'romantest6.py',
 'romantest7.py',
 'romantest8.py',
 'romantest9.py']  
  1. Модуль glob принимает подстановочный знак и возвращает путь ко всем файлам и каталогам, соответствующим подстановочному знаку. В этом примере подстановочный знак - это путь к каталогу плюс « * .xml », который будет соответствовать всем файлам .xml в подкаталоге examples .
  2. Теперь измените текущий рабочий каталог на подкаталог examples . Функция os.chdir () может принимать относительные пути.
  3. В шаблон глобуса можно включить несколько подстановочных знаков. Этот пример находит все файлы в текущем рабочем каталоге, которые имеют расширение .py и содержат слово test в любом месте своего имени файла.

Получение метаданных файла

Каждая современная файловая система хранит метаданные о каждом файле: дату создания, дату последнего изменения, размер файла и так далее.Python предоставляет единый API для доступа к этим метаданным. Файл открывать не нужно; все, что вам нужно, это имя файла.

  >>>   импорт ОС 
  >>>   печать (os.getcwd ())  c: \ Users \ pilgrim \ diveintopython3 \ examples 
  >>>   метаданные = os.stat ('feed.xml')  >>>   metadata.st_mtime  1247520344.9537716 
  >>>   время импорта  >>>   раз.местное время (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) 
 
  1. Текущий рабочий каталог - это папка examples .
  2. feed.xml - это файл в папке examples . Вызов функции os.stat () возвращает объект, который содержит несколько различных типов метаданных о файле.
  3. st_mtime - время изменения, но оно в не очень полезном формате.(Технически, это количество секунд, прошедшее с начала Эпохи, которая определяется как первая секунда 1 января 1970 года. Серьезно.)
  4. Модуль time является частью стандартной библиотеки Python. Он содержит функции для преобразования между различными представлениями времени, форматирования значений времени в строки и работы с часовыми поясами.
  5. Функция time.localtime () преобразует значение времени из секунд с начала эпохи (из свойства st_mtime , возвращенного из os.stat () ) в более удобную структуру года, месяца, дня, часа, минуты, секунды и т. д. В последний раз этот файл был изменен 13 июля 2009 г., около 17:25.
# продолжение предыдущего примера
  >>>   metadata.st_size  3070 
  >>>   импорт человеческий 
  >>>   humanize.approximate_size (metadata.st_size)  '3,0 КБ'  
  1. ОС Функция stat () также возвращает размер файла в свойстве st_size . Размер файла feed.xml составляет 3070 байта.
  2. Вы можете передать свойство st_size в функцию приближаться к размеру () .

Построение абсолютных путей

В предыдущем разделе функция glob.glob () вернула список относительных имен путей. В первом примере были пути вроде 'examples \ feed.xml ', а во втором примере относительные пути были короче, например ' romantest1.py '. Пока вы остаетесь в том же текущем рабочем каталоге, эти относительные пути будут работать для открытия файлов или получения метаданных файлов. Но если вы хотите создать абсолютный путь - , то есть , который включает все имена каталогов обратно в корневой каталог или букву диска - тогда вам понадобится функция os.path.realpath () .

  >>>   импорт ОС 
  >>>   печать (ос.getcwd ()) 
  c: \ Users \ pilgrim \ diveintopython3 \ examples 
  >>>   печать (os.path.realpath ('feed.xml')) 
  c: \ Users \ pilgrim \ diveintopython3 \ examples \ feed.xml  

Составление списка

Вы можете использовать любое выражение Python в понимании списка.

Понимание списка обеспечивает компактный способ отображения списка в другой список путем применения функции к каждому из элементов списка.

  >>>   a_list = [1, 9, 8, 4] 
  >>>   [элемент * 2 для элемента в a_list]  [2, 18, 16, 8] 
  >>>   a_list  [1, 9, 8, 4] 
  >>>   a_list = [elem * 2 для элемента в a_list]  >>>   a_list 
  [2, 18, 16, 8]  
  1. Чтобы понять это, посмотрите на это справа налево. a_list - это список, который вы сопоставляете.Интерпретатор Python просматривает a_list по одному элементу за раз, временно присваивая значение каждого элемента переменной elem . Затем Python применяет функцию elem * 2 и добавляет этот результат в возвращаемый список.
  2. Понимание списка создает новый список; он не меняет исходный список.
  3. Можно безопасно присвоить результат понимания списка переменной, которую вы сопоставляете. Python создает новый список в памяти и, когда понимание списка завершено, присваивает результат исходной переменной.

Вы можете использовать любое выражение Python в понимании списка, включая функции в модуле os для управления файлами и каталогами.

  >>>   импорт ОС, глоб 
  >>>   glob.glob ('*. Xml')  ['feed-broken.xml', 'feed-ns0.xml', 'feed.xml'] 
  >>>   [os.path.realpath (f) для f в 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'] 
 
  1. Это возвращает список всех файлов .xml, в текущем рабочем каталоге.
  2. Это понимание списка берет этот список из файлов .xml и и преобразует его в список полных имен путей.

Составные части списка также могут фильтровать элементы, давая результат, который может быть меньше исходного списка.

  >>>   импорт ОС, глоб 
  >>>   [f вместо f в glob.glob ('*. Py'), если os.stat (f) .st_size> 6000]  ['pluraltest6.py',
 'romantest10.py',
 'romantest6.py',
 'romantest7.py',
 'romantest8.py',
 'romantest9.py'] 
 
  1. Чтобы отфильтровать список, вы можете включить предложение if в конец понимания списка. Выражение после ключевого слова if будет оцениваться для каждого элемента в списке.Если выражение оценивается как True , элемент будет включен в вывод. Это понимание списка просматривает список всех файлов .py, в текущем каталоге, а выражение if фильтрует этот список, проверяя, превышает ли размер каждого файла 6000 байта. Таких файлов шесть, поэтому составление списка возвращает список из шести имен файлов.

Все примеры составления списков до сих пор содержали простые выражения - умножение числа на константу, вызов одной функции или просто возврат исходного элемента списка (после фильтрации).Но нет предела тому, насколько сложным может быть понимание списка.

  >>>   импорт ОС, глоб 
  >>>   [(os.stat (f) .st_size, os.path.realpath (f)) для f в 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')] 
  >>>   импорт человеческий 
  >>>   [(человеческий.приближенный_размер (os.stat (f) .st_size), f) для f в glob.glob ('*. xml')]  [('3,0 КиБ', 'feed-broken.xml'),
 ('3,3 КиБ', 'feed-ns0.xml'),
 ('3,0 КБ', 'feed.xml')]  
  1. Это понимание списка находит все файлы .xml в текущем рабочем каталоге, получает размер каждого файла (путем вызова функции os.stat () ) и строит кортеж размера файла и абсолютного путь к каждому файлу (вызывая функцию os.path.realpath () ).
  2. Это понимание основано на предыдущем, чтобы вызвать функцию приближенно_size () с размером каждого файла .xml .

Словарь терминов

Понимание словаря похоже на понимание списка, но строит словарь вместо списка.

  >>>   импорт ОС, глоб 
  >>>   метаданные = [(f, os.stat (f)) для f в glob.glob ('* test * .py')]  >>>   метаданные [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,
 st_mtime = 1247520344, st_ctime = 1247520344)) 
  >>>   metadata_dict = {f: os.stat (f) for f в glob.glob ('* test * .py')}  >>>   тип (metadata_dict)  <класс 'dict'> 
  >>>   список (metadata_dict.ключи ())  ['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'] 
  >>>   metadata_dict ['alphameticstest.py']. St_size  2509  
  1. Это не понимание словаря; это понимание списка.Он находит все файлы .py, с test в их имени, а затем создает кортеж из имени файла и метаданных файла (из вызова функции os.stat () ).
  2. Каждый элемент результирующего списка представляет собой кортеж.
  3. Это словарное понимание. Синтаксис подобен пониманию списка с двумя отличиями. Во-первых, он заключен в фигурные скобки вместо квадратных. Во-вторых, вместо одного выражения для каждого элемента он содержит два выражения, разделенных двоеточием.Выражение перед двоеточием ( f в этом примере) является ключом словаря; выражение после двоеточия ( os.stat (f) в этом примере) является значением.
  4. Понимание словаря возвращает словарь.
  5. Ключи этого конкретного словаря - это просто имена файлов, возвращенные при вызове glob.glob ('* test * .py') .
  6. Значение, связанное с каждым ключом, является значением, возвращаемым функцией os.stat () .Это означает, что мы можем «искать» файл по имени в этом словаре, чтобы получить его метаданные. Одна из частей метаданных - это st_size , размер файла. Файл alphameticstest.py имеет длину 2509 байта.

Как и в случае со списками, вы можете включить предложение if в понимание словаря для фильтрации входной последовательности на основе выражения, которое оценивается с каждым элементом.

  >>>   import os, glob, humanize 
  >>>   metadata_dict = {f: os.stat (f) для f в glob.glob ('*')}  >>>   humanize_dict = {os.path.splitext (f) [0]: humanize.approximate_size (meta.st_size) \   ...   для f, мета в metadata_dict.items (), если мета. st_size> 6000}  >>>   список (humanize_dict.keys ())  ['romantest9', 'romantest8', 'romantest7', 'romantest6', 'romantest10', 'pluraltest6'] 
  >>>   humanize_dict ['romantest9']  '6.5 КБ ' 
  1. Это понимание словаря создает список всех файлов в текущем рабочем каталоге ( glob.glob ('*') ), получает метаданные файла для каждого файла ( os.stat (f) ) и создает словарь, ключами которого являются имена файлов, а значениями - метаданные для каждого файла.
  2. Это понимание словаря основано на предыдущем понимании, отфильтровывает файлы размером менее 6000 байта (, если meta.st_size> 6000 ) и использует этот отфильтрованный список для создания словаря, ключи которого представляют собой имя файла без расширения ( os .path.splitext (f) [0] ), значения которого являются приблизительным размером каждого файла ( humanize.approximate_size (meta.st_size) ).
  3. Как вы видели в предыдущем примере, таких файлов шесть, следовательно, в этом словаре шесть элементов.
  4. Значение каждого ключа - это строка, возвращаемая функцией приближаться к размеру () .

Другие забавные вещи, связанные с пониманием словаря

Вот трюк с пониманием словаря, который когда-нибудь может пригодиться: замена ключей и значений словаря.

  >>>   a_dict = {'a': 1, 'b': 2, 'c': 3} 
  >>>   {значение: ключ для ключа, значение в a_dict.items ()} 
  {1: 'a', 2: 'b', 3: 'c'}  

Конечно, это работает, только если значения словаря неизменяемы, например строки или кортежи. Если вы попробуете сделать это со словарем, содержащим списки, он потерпит наибольшую неудачу.

  >>>   a_dict = {'a': [1, 2, 3], 'b': 4, 'c': 5} 
  >>>   {значение: ключ для ключа, значение в a_dict.items ()} 
  Traceback (последний звонок последний):
  Файл "", строка 1, в 
  Файл "", строка 1, в 
TypeError: unhashable type: 'list'  

Набор понятий

Не будем забывать, что наборы также имеют свой собственный синтаксис понимания. Он очень похож на синтаксис словаря. Единственное отличие состоит в том, что наборы имеют просто значения вместо пар ключ: значение.

  >>>   a_set = set (диапазон (10)) 
  >>>   a_set 
  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 
  >>>   {x ** 2 для x в a_set}  {0, 1, 4, 81, 64, 9, 16, 49, 25, 36} 
  >>>   {x для x в a_set, если x% 2 == 0}  {0, 8, 2, 4, 6} 
  >>>   {2 ** x для x в диапазоне (10)}  {32, 1, 2, 4, 8, 64, 128, 256, 16, 512} 
 
  1. Компоненты набора могут принимать набор в качестве входных данных.Это понимание набора вычисляет квадраты набора чисел от 0 до 9 .
  2. Подобно пониманию списка и пониманию словаря, понимание набора может содержать предложение if для фильтрации каждого элемента перед его возвратом в набор результатов.
  3. Компоненты набора не должны принимать набор в качестве входных данных; они могут взять любую последовательность.

Дополнительная литература

  • os модуль
  • os - переносимый доступ к особенностям операционной системы
  • ос.путь модуль
  • os.path - Платформенно-независимые манипуляции с именами файлов
  • glob модуль
  • glob - сопоставление имени файла с шаблоном
  • время модуль
  • время - Функции для управления временем на часах
  • Составление списка
  • Понятия вложенных списков
  • Техника зацикливания

☜ ☞

© 2001–11 Марк Пилигрим

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *