Программирование на Python 3. Руководство издательство СимволПлюс
Скачать 3.74 Mb.
|
111 if word != 0: print_unicode_table(word) После инструкций импортирования и определения функции print_uni code_table() выполнение достигает программного кода, показанного выше. Сначала предположим, что пользователь не указал в командной строке искомое слово. Если аргумент командной строки присутствует и это – h или –– help , программа выводит информацию о порядке исполь зования и устанавливает флаг word в значение 0, указывая тем самым, что работа завершена. В противном случае в переменную word записы вается копия аргумента, введенного пользователем, с преобразовани ем всех символов в нижний регистр. Если значение word не равно 0, программа выводит таблицу. При выводе информации о порядке использования применяется спе цификатор формата, который представляет собой простое имя форма та, в данном случае – порядковый номер позиционного аргумента. Мы могли бы записать эту строку, как показано ниже: print("usage: {0[0]} [string]".format(sys.argv)) При таком подходе первый символ 0 соответствует порядковому номе ру позиционного аргумента, а [0] – это индекс элемента внутри аргу мента, и такой прием сработает, потому что sys.argv является списком. def print_unicode_table(word): print("decimal hex chr {0:^40}".format("name")) print(" {0:<40}".format("")) code = ord(" ") end = sys.maxunicode while code < end: c = chr(code) name = unicodedata.name(c, "*** unknown ***") if word is None or word in name.lower(): print("{0:7} {0:5X} {0:^3c} {1}".format( code, name.title())) code += 1 Мы использовали пару пустых строк исключительно для улучшения удобочитаемости. Первые две строки функции выводят строки заго ловка. Первый вызов str.format() выводит текст «name», отцентриро ванный в поле вывода, шириной 40 символов, а второй вызов выводит пустую строку в поле шириной 40 символов, используя символ « – » в качестве символазаполнителя, с выравниванием по левому краю. (Мы вынуждены указывать символ выравнивания, когда задается сим волзаполнитель.) Как вариант, вторую строку функции можно было записать, как показано ниже: print(" {0}".format("" * 40)) 112 Глава 2. Типы данных Здесь мы использовали оператор дублирования строки (*), чтобы соз дать необходимую строку, и просто вставили ее в строку формата. В третьем случае можно было бы просто ввести 40 символов « – » и ис пользовать простой литерал строки. Текущий код символа Юникода сохраняется в перемен ной code, которая инициализируется кодом пробела (0x20). В переменную end записывается максимально воз можный код символа Юникода, который может прини мать разные значения в зависимости от того, какая из кодировок (UCS2 или UCS4) использовалась при ком пиляции Python. Внутри цикла while с помощью функции chr() мы получаем символ Юникода, соответствующий числовому коду. Функция unicodedata.na me() возвращает название заданного символа Юникода, во втором не обязательном аргументе передается имя, которое будет использовано в случае, когда имя символа не определено. Если пользователь не указывает аргумент командной строки (word is None ) или аргумент был указан и он входит в состав копии имени сим вола Юникода, в которой все символы приведены к нижнему регист ру, то выводится соответствующая строка таблицы. Мы передаем переменную code методу str.format() один раз, но в стро ке формата она используется трижды. Первый раз – при выводе значе ния code как целого числа в поле с шириной 7 символов (по умолчанию в качестве символазаполнителя используется пробел, поэтому нет не обходимости явно указывать его). Второй раз – при выводе значения code как целого числа в шестнадцатеричном формате символами верх него регистра в поле шириной 5 символов. И третий раз – при выводе символа Юникода, соответствующего значению code, с помощью спе цификатора формата «c», отцентрированного в поле с минимальной шириной 3 символа. Обратите внимание, что нам не потребовалось указывать тип «d» в первом спецификаторе формата, потому что он подразумевается по умолчанию для целых чисел. Второй аргумент – это имя символа Юникода, которое выводится с помощью метода str.title() , в результате которого первый символ каждого слова пре образуется к верхнему регистру, а остальные символы – к нижнему. Теперь, когда мы познакомились с универсальным методом str.for mat() , мы можем с успехом использовать его на протяжении остальной части книги. Кодировки символов Так или иначе, но компьютеры могут хранить информацию только в виде байтов, то есть в виде 8битовых значений в диапазоне от 0x00 до 0xFF . Каждый символ должен быть представлен некоторым образом в терминах байтов. На заре развития вычислительной техники были Кодировки символов, стр. 112 Строки 113 разработаны схемы кодирования символов, в которых каждому кон кретному был поставлен в соответствие байт с конкретным значением. Например, в кодировке ASCII символ A представлен байтом со значе нием 0x41, B – 0x42 и т. д. В Западной Европе часто использовалась ко дировка Latin1, ее первые 127 символов совпадали с 7битовой коди ровкой ASCII, а остальные значения представляли символы с умляу тами и другие символы, необходимые европейцам. За долгие годы поя вилось множество кодировок, которые используются до сих пор. К сожалению, наличие такого разнообразия кодировок оказалось очень неудобным, особенно при разработке интернационализируемого программного обеспечения. Одним из решений, которое было принято практически повсеместно, стало применение кодировки Юникод. В ко дировке Юникод каждому символу ставится в соответствие целое чис ло, то есть его код, как и в кодировках, разработанных ранее, но Юни код не ограничивается использованием одного байта на символ и пото му способен обеспечить единую систему представления каждого сим вола любого языка. А для обеспечения обратной совместимости первые 127 символов Юникода совпадают со 127 символами 7битовой коди ровки ASCII. Но как хранятся символы Юникода? В настоящее время определено немногим более 1 миллиона символов Юникода, поэтому даже 32би товых целых чисел со знаком более чем достаточно для представления любого кода в кодировке Юникод. Таким образом, самый простой спо соб хранения символов Юникода заключается в использовании после довательностей 32битовых целых чисел, по одному целому числу на символ. Такое представление очень удобно хранить в памяти, потому что мы получаем массив 32битовых чисел, элементы которого имеют однозначное соответствие с символами. Но тогда если текст в файлах или передаваемый по сети в основном содержит символы 7битовой кодировки ASCII, то три из четырех переданных байтов будут нулевы ми (0x00). Чтобы избежать появления такого большого объема ненуж ной информации, сама кодировка Юникод имеет несколько представ лений. В памяти символы Юникода хранятся либо в формате UCS2 (по сути, 16битовые целые беззнаковые числа), способном представить первые 65 535 кодов символов, или в формате UCS4 (32битовые целые чис ла), способном представить все коды символов, которых к моменту на писания этих строк было 1 114 111. Выбор того или иного формата производится на этапе компиляции PyП122 thon. (Если значение sys.maxunicode равно 65 535, значит Python ком пилировался с поддержкой формата UCS2). При сохранении данных в виде файлов или при передаче по сети ис пользуется более сложный формат представления. При использовании Юникода коды символов могут кодироваться с помощью кодировки UTF8, в которой первые 127 символов кодируются однобайтовыми 114 Глава 2. Типы данных значениями, а остальные – двумя или более байтами. Кодировка UTF8 очень компактна для английского текста, и если в нем используются только символы из 7битового набора ASCII, то файлы с текстом в ко дировке UTF8 ничем не отличаются от файлов в кодировке ASCII. Другая популярная кодировка – UTF16. В ней для кодирования зна чительной части символов используются два байта и для остальных – четыре байта. Она более компактна для некоторых азиатских языков, чем UTF8, но, в отличие от нее, текст в кодировке UTF16 должен на чинаться с признака, указывающего порядок следования байтов, что бы при чтении кодов можно было определить, какой порядок следова ния пар байтов используется – прямой (bigendian) или обратный (litt leendian). Кроме того, попрежнему широко используются старые ко дировки, такие как GB2312, ISO88595, Latin1. Метод str.encode() возвращает последовательность байтов, фактиче ски – объект типа bytes, о котором будет рассказываться в главе 7, за кодированных в соответствии с кодировкой, заданной в качестве аргу мента. С помощью этого метода можно глубже понять различия между кодировками и понять, почему неправильные предположения о коди ровке могут приводить к появлению ошибок: >>> artist = "Tage ° Ase Ђ n" >>> artist.encode("Latin1") b'Tage \xc5s\xe9n' >>> artist.encode("CP850") b'Tage \x8fs\x82n' >>> artist.encode("utf8") b'Tage \xc3\x85s\xc3\xa9n' >>> artist.encode("utf16") b'\xff\xfeT\x00a\x00g\x00e\x00 \x00\xc5\x00s\x00\xe9\x00n\x00' Символ «b» перед открывающей кавычкой указывает, что это не стро ковый литерал, а литерал типа bytes. Для удобства при создании лите ралов типа bytes мы можем смешивать печатаемые символы ASCII с экранированными шестнадцатеричными значениями. Мы не можем представить имя «Tage ° AseЂ n» с помощью символов ASCII, потому что в этом наборе отсутствует символ « ° A », как и любые другие символы с умляутами, поэтому при попытке сделать это возбу ждается исключение UnicodeEncodeError. Кодировка Latin1 (известная так же, как ISO88591) использует для представления символов 8би товые значения, и в ней присутствуют все символы, необходимые для представления данного имени. С другой стороны, артисту Erno Ђ Ђ Bа nk повезло меньше, так как символ «o Ђ Ђ » отсутствует в наборе символов Latin1. Конечно, оба имени благополучно могут быть представлены в кодировке Юникод. Примечательно, что при использовании коди ровки UTF16 первые два байта являются признаком порядка следова ния байтов – они используются функцией декодирования, чтобы опре делить, какой порядок следования используется, прямой или обрат ный, и выполнить декодирование соответствующим образом. Строки 115 Следует отметить пару важных особенностей, присущих методу str.en code() . Первый аргумент (имя кодировки) не чувствителен к регистру символов, а символы дефиса и подчеркивания в имени считаются эк вивалентными, поэтому имена «usascii» и «US_ASCII» рассматрива ются как одно и то же имя. Кроме того, для одной и той же кодировки может иметься множество альтернативных названий: например, на звания «latin», «latin1», «latin_1», «ISO88591», «CP819» и некото рые другие обозначают кодировку «Latin1». Метод может также при нимать второй необязательный аргумент, который сообщает, как сле дует обрабатывать ошибки. Например, мы можем закодировать лю бую строку в кодировке ASCII, передав во втором аргументе «ignore» или «replace» – ценой потери данных, или без потерь, передав строку «backslashreplace» – в этом случае символы, не входящие в набор ASCII, будут представлены последовательностями \x, \u и \U. Напри мер, вызов artist.encode("ascii", "ignore") вернет b'Tage sn', вызов art ist.encode("ascii", "replace") вернет b'Tage ?s?n', а вызов artist.enco de("ascii", "backslashreplace") вернет b'Tage \xc5s\xe9n'. (Точно так же мы могли бы получить строку ASCIIсимволов с помощью вызова "{0!a}".format(artist) , который вернет 'Tage \xc5s\xe9n'.) В дополнение к методу str.encode() имеется метод bytes.decode() (а так же bytearray.decode()), который возвращает строку с байтами, декоди рованными при помощи заданной кодировки. Например: >>> print(b"Tage \xc3\x85s\xc3\xa9n".decode("utf8")) Tage ° Ase Ђ n >>> print(b"Tage \xc5s\xe9n".decode("latin1")) Tage ° Ase Ђ n Различия между 8битовыми кодировками Latin1, CP850 (кодировка IBM PC) и кодировкой UTF8 очевидно доказывают, что выбор коди ровки наугад едва ли может считаться успешной стратегией. К сча стью, кодировка UTF8 фактически уже стала стандартом для про стых текстовых файлов, благодаря чему следующие поколения, веро ятно, даже не узнают, что некогда существовали другие кодировки. Для файлов с расширением .py используется кодировка UTF8, поэто му Python всегда знает, какая кодировка используется для представ ления строковых литералов. Это означает, что мы можем использо вать в своих строках любые символы Юникода, поддерживаемые на шим текстовым редактором. 1 Когда Python читает данные из внешних источников, например из сете вых сокетов, он не может заранее знать, какая кодировка используется, 1 Вполне возможно использовать и другие кодировки. Подробности можно найти в документе «Python Tutorial», в разделе «Source Code Encoding». (http://docs.python.org/3.0/tutorial/interpreter.html#sourcecodeencoding. – Прим. перев. ) 116 Глава 2. Типы данных поэтому он возвращает байты, которые мы можем декодировать нуж ным образом. В отношении текстовых файлов Python использует более дружественный подход, используя локальную кодировку, если мы не указываем ее явно. К счастью, некоторые форматы файлов явно определяют свою коди ровку. Например, мы можем предположить, что XMLфайл использу ет кодировку UTF8, если в нем отсутствует директива , явно указывающая другую кодировку. Поэтому при чтении XMLфайлов мы можем извлекать, скажем первые 1000 байтов, отыскивать опреде ление кодировки и, если оно присутствует, декодировать содержимое файла в соответствии с указанной кодировкой; в противном случае пе реходить на использование кодировки UTF8. Такой прием должен срабатывать для любых XMLфайлов или простых текстовых файлов, в которых используется любая из однобайтовых кодировок, поддержи ваемых Python, за исключением кодировок, основанных на EBCDIC (CP424, CP500), и некоторых других (CP037, CP864, CP865, CP1026, CP1140, HZ, SHIFTJIS2004, SHIFTJISX0213). К сожалению, такой подход неприменим в случае использования многобайтовых кодиро вок (таких как UTF16 и UTF32). В каталоге пакетов Python (Python Package Index), pypi.python.org/pypi, имеется по крайней мере два па кета, позволяющих автоматически определять кодировку файлов. Примеры В этом разделе мы будем использовать знания, полученные в этой и в предыдущей главах, чтобы представить две маленькие, но закон ченные программы, соединяющие в себе все, что мы узнали до сих пор. Первая программа имеет некоторое отношение к математике, но она очень короткая и занимает всего 35 строк. Вторая связана с обработ кой текста и имеет более существенный объем – в ней определяется семь функций и содержит она около 80 строк программного кода. quadratic.py Квадратные уравнения – это уравнения вида ax 2 + bx + c = 0, где a ≠ 0, описывающие параболу. Корни таких уравнений находятся по фор муле Часть формулы b 2 – 4ac называется дискриминантом – если это поло жительная величина, уравнение имеет два действительных корня, ес ли дискриминант равен нулю – уравнение имеет один действительный корень, и в случае отрицательного значения уравнение имеет два ком плексных корня. Мы напишем программу, которая будет принимать x –b b 2 –4ac ± 2a = Примеры 117 от пользователя коэффициенты a, b и c (коэффициенты b и c могут быть равны нулю) и затем вычислять и выводить его корень или корни. 1 Для начала посмотрим, как работает программа, а потом перейдем к изучению программного кода. quadratic.py ax 2 + bx + c = 0 enter a: 2.5 enter b: 0 enter c: 7.25 2.5x 2 + 0.0x + 7.25 = 0 → x = 1.70293863659 or x = 1.70293863659 С коэффициентами 1.5, – 3 и 6 программа выведет (некоторые цифры обрезаны): 1.5x 2 + 3.0x + 6.0 = 0 → x = (1+1.7320508j) or x = (11.7320508j) Вывод программы не так хорош, как хотелось бы – например, вместо + – 3.0x лучше было бы выводить – 3.0x , а коэффициенты, равные нулю, – вообще не показывать. Вы получите шанс ликвидировать эти недос татки при выполнении упражнений. Теперь обратимся к программному коду, который начинается тремя инструкциями import: import cmath import math import sys Нам необходимы обе математические библиотеки для работы с числа ми типа float и complex, так как функции, вычисляющие квадратный корень из вещественных и комплексных чисел, отличаются. Модуль sys нам необходим, так как в нем определена константа sys.float_in fo.epsilon , которая потребуется нам для сравнения вещественных чи сел со значением 0. Нам также необходима функция, которая будет получать от пользова теля число с плавающей точкой: def get_float(msg, allow_zero): x = None while x is None: try: x = float(input(msg)) 1 Поскольку консоль в операционной системе Windows имеет весьма ограни ченную поддержку UTF8, а консоль в системе Mac OS X по умолчанию ис пользует кодировку Apple Roman, существует две проблемы с символами 2 и →, которые используются программой quadratic.py. Мы включили в при меры файл quadratic_uni.py, который отображает корректные символы в консоли Linux и использует их заменители (^2 и >) в других системах. 118 Глава 2. Типы данных if not allow_zero and abs(x) < sys.float_info.epsilon: print("zero is not allowed") x = None except ValueError as err: print(err) return x Эта функция выполняет цикл, пока пользователь не введет допустимое число с плавающей точкой (например, 0.5, – 9, 21, 4.92), и допускает ввод значения 0, только если аргумент allow_zero имеет значение True. Вслед за определением функции get_float() выполняется оставшаяся часть программного кода. Мы разделим его на три части и начнем со взаимодействия с пользователем: print("ax\N{SUPERSCRIPT TWO} + bx + c = 0") a = get_float("enter a: ", False) b = get_float("enter b: ", True) c = get_float("enter c: ", True) Благодаря функции get_float() получить значения коэффициентов a, b и c оказалось очень просто. Второй аргумент функции сообщает, ко гда значение 0 является допустимым. x1 = None x2 = None discriminant = (b ** 2) (4 * a * c) if discriminant == 0: x1 = (b / (2 * a)) else: if discriminant > 0: root = math.sqrt(discriminant) else: # discriminant < 0 root = cmath.sqrt(discriminant) x1 = (b + root) / (2 * a) x2 = (b root) / (2 * a) Программный код выглядит несколько иначе, чем формула, потому что мы начали вычисления с определения значения дискриминанта. Если дискриминант равен 0, мы знаем, что уравнение имеет единствен ное действительное решение и можно сразу же вычислить его. В про тивном случае мы вычисляем действительный или комплексный квад ратный корень из дискриминанта и находим два корня уравнения. equation = ("{0}x\N{SUPERSCRIPT TWO} + {1}x + {2} = 0" " \N{RIGHTWARDS ARROW} x = {3}").format(a, b, c, x1) if x2 is not None: equation += " or x = {0}".format(x2) print(equation) Мы не использовали скольконибудь сложного форматирования, по скольку форматирование, используемое по умолчанию для чисел с пла вающей точкой в языке Python, прекрасно подходит для этого приме |