Краткий обзор стандартной библиотеки — Часть II
Этот второй обзор охватывает более продвинутые модули, которые поддерживают профессиональные потребности программирования. Эти модули редко встречаются в небольших скриптах.
11.1. Форматирование вывода
Модуль reprlib предоставляет версию repr(), настроенную для сокращенного отображения больших или глубоко вложенных контейнеров:
>>> import reprlib
>>> reprlib.repr(set('supercalifragilisticexpialidocious'))
"{'a', 'c', 'd', 'e', 'f', 'g', ...}"
Модуль pprint предлагает более сложный контроль над выводом как встроенных, так и пользовательских объектов таким образом, чтобы он был удобочитаем для интерпретатора. Когда результат занимает более одной строки, «красивый принтер» добавляет разрывы строк и отступы, чтобы более четко показать структуру данных:
>>> import pprint >>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', ... 'yellow'], 'blue']]] ... >>> pprint.pprint(t, width=30) [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', 'yellow'], 'blue']]]
Модуль textwrap форматирует абзацы текста для соответствия заданной ширине экрана:
>>> import textwrap >>> doc = """The wrap() method is just like fill() except that it returns ... a list of strings instead of one big string with newlines to separate ... the wrapped lines.""" ... >>> print(textwrap.fill(doc, width=40)) The wrap() method is just like fill() except that it returns a list of strings instead of one big string with newlines to separate the wrapped lines.
Модуль locale получает доступ к базе данных форматов данных, специфичных для культуры. Атрибут группирования функции форматирования locale предоставляет прямой способ форматирования чисел с разделителями группировки:
>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
'English_United States.1252'
>>> conv = locale.localeconv() # get a mapping of conventions
>>> x = 1234567.8
>>> locale.format_string("%d", x, grouping=True)
'1,234,567'
>>> locale.format_string("%s%.*f", (conv['currency_symbol'],
... conv['frac_digits'], x), grouping=True)
'$1,234,567.80'
11.2. Шаблоны
Модуль string включает в себя универсальный класс Template с упрощенной синтаксической конструкцией, удобной для редактирования конечными пользователями. Это позволяет пользователям настраивать свои приложения без изменения самого приложения.
Формат использует имена заменителей, образованные $ допустимыми идентификаторами Python (буквенно-цифровые символы и символы подчеркивания). Окружение заменителя фигурными скобками позволяет ему следовать за дополнительными буквенно-цифровыми символами без промежутков. Написание $$ создает один экранированный $:
>>> from string import Template
>>> t = Template('${village}folk send $$10 to $cause.')
>>> t.substitute(village='Nottingham', cause='the ditch fund')
'Nottinghamfolk send $10 to the ditch fund.'
Метод substitute() генерирует исключение KeyError, когда заполнитель не указан в словаре или в качестве именованного аргумента. В приложениях типа почтовой рассылки пользовательские данные могут быть неполными, и метод safe_substitute() может быть более подходящим — он оставит заполнитель без изменений, если данные отсутствуют:
>>> t = Template('Return the $item to $owner.')
>>> d = dict(item='unladen swallow')
>>> t.substitute(d)
Traceback (most recent call last):
...
KeyError: 'owner'
>>> t.safe_substitute(d)
'Return the unladen swallow to $owner.'
Подклассы шаблонов могут указывать пользовательский разделитель. Например, программа для пакетного переименования фотографий для просмотрщика фотографий может выбрать использование знаков процента для заменителей, таких как текущая дата, номер последовательности изображений или формат файла:
>>> import time, os.path
>>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']
>>> class BatchRename(Template):
... delimiter = '%'
...
>>> fmt = input('Enter rename style (%d-date %n-seqnum %f-format): ')
Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f
>>> t = BatchRename(fmt)
>>> date = time.strftime('%d%b%y')
>>> for i, filename in enumerate(photofiles):
... base, ext = os.path.splitext(filename)
... newname = t.substitute(d=date, n=i, f=ext)
... print('{0} --> {1}'.format(filename, newname))
img_1074.jpg --> Ashley_0.jpg
img_1076.jpg --> Ashley_1.jpg
img_1077.jpg --> Ashley_2.jpg
Еще одно применение шаблонов заключается в отделении логики программы от деталей нескольких форматов вывода. Это позволяет подставить пользовательские шаблоны для XML-файлов, текстовых отчетов и HTML-веб-отчетов.
11.3. Работа с двоичными данными и структурами записей
Модуль struct предоставляет функции pack() и unpack() для работы с двоичными форматами записей переменной длины. Следующий пример демонстрирует, как проходить по заголовкам информации в файле ZIP без использования модуля zipfile. Коды упаковки "H" и "I" соответственно представляют целые беззнаковые числа с размером в два и четыре байта. "<" указывает, что они имеют стандартный размер и порядок байтов little-endian:
import struct
with open('myfile.zip', 'rb') as f:
data = f.read()
start = 0
for i in range(3): # show the first 3 file headers
start += 14
fields = struct.unpack('<IIIHH', data[start:start+16])
crc32, comp_size, uncomp_size, filenamesize, extra_size = fields
start += 16
filename = data[start:start+filenamesize]
start += filenamesize
extra = data[start:start+extra_size]
print(filename, hex(crc32), comp_size, uncomp_size)
start += extra_size + comp_size # skip to the next header
11.4. Многопоточность
Многопоточность — это техника для разделения задач, которые не зависят друг от друга в последовательном порядке. Потоки могут быть использованы для повышения отзывчивости приложений, которые принимают пользовательский ввод, в то время как другие задачи выполняются в фоновом режиме. Сопутствующим случаем использования является одновременное выполнение операций ввода-вывода и вычислений в другом потоке.
Следующий код демонстрирует, как высокоуровневый модуль threading может запускать задачи в фоновом режиме, в то время как основная программа продолжает выполняться:
import threading, zipfile
class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile
def run(self):
f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print('Finished background zip of:', self.infile)
background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print('The main program continues to run in foreground.')
background.join() # Wait for the background task to finish
print('Main program waited until background was done.')
Основной проблемой многопоточных приложений является координация потоков, которые разделяют данные или другие ресурсы. Для этой цели модуль threading предоставляет ряд синхронизационных примитивов, включая блокировки, события, переменные состояния и семафоры.
Хотя эти инструменты мощны, незначительные ошибки в проектировании могут привести к проблемам, которые трудно воспроизвести. Поэтому предпочтительный подход к координации задач заключается в концентрации всего доступа к ресурсу в одном потоке, а затем использовании модуля queue для подачи запросов из других потоков в этот поток. Приложения, использующие объекты Queue для межпоточной коммуникации и координации, легче проектируются, более удобочитаемы и надежнее.
11.5. Ведение журнала
Модуль logging предлагает полную и гибкую систему ведения журнала. В самом простом виде сообщения журнала отправляются в файл или в sys.stderr:
import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')
Это приводит к следующему выводу:
WARNING:root:Warning:config file server.conf not found ERROR:root:Error occurred CRITICAL:root:Critical error -- shutting down
По умолчанию информационные и отладочные сообщения подавляются, и вывод отправляется в стандартный поток ошибок. Другие варианты вывода включают перенаправление сообщений через электронную почту, дейтаграммы, сокеты или веб-сервер HTTP. Новые фильтры могут выбирать различные маршруты на основе приоритета сообщений: DEBUG, INFO, WARNING, ERROR и CRITICAL.
Система ведения журнала может настраиваться непосредственно из Python или загружаться из файла конфигурации, редактируемого пользователем, для настраиваемого ведения журнала без изменения приложения.
11.6. Слабые ссылки
Python выполняет автоматическое управление памятью (счётчик ссылок для большинства объектов и сбор мусора для удаления циклов). Память освобождается вскоре после того, как последняя ссылка на неё будет устранена.
Этот подход хорошо работает для большинства приложений, но иногда возникает необходимость отслеживать объекты только до тех пор, пока они используются чем-то ещё. К сожалению, просто отслеживание их создаёт ссылку, которая делает их постоянными. Модуль weakref предоставляет инструменты для отслеживания объектов без создания ссылки. Когда объект больше не нужен, он автоматически удаляется из таблицы слабых ссылок, и для объектов слабых ссылок срабатывает обратный вызов. Типичные применения включают кеширование объектов, которые дорого создавать:
>>> import weakref, gc
>>> class A:
... def __init__(self, value):
... self.value = value
... def __repr__(self):
... return str(self.value)
...
>>> a = A(10) # create a reference
>>> d = weakref.WeakValueDictionary()
>>> d['primary'] = a # does not create a reference
>>> d['primary'] # fetch the object if it is still alive
10
>>> del a # remove the one reference
>>> gc.collect() # run garbage collection right away
0
>>> d['primary'] # entry was automatically removed
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
d['primary'] # entry was automatically removed
File "C:/python313/lib/weakref.py", line 46, in __getitem__
o = self.data[key]()
KeyError: 'primary'
11.7. Инструменты для работы со списками
Многие потребности в структурах данных могут быть удовлетворены встроенным типом списка. Однако иногда требуется альтернативная реализация с различными компромиссами в производительности.
Модуль array предоставляет объект array, который похож на список, но хранит только однородные данные и хранит их более компактно. Следующий пример демонстрирует массив чисел, хранящихся как двухбайтовые беззнаковые двоичные числа (код типа "H") вместо обычных 16 байтов на запись для обычных списков объектов Python int:
>>> from array import array
>>> a = array('H', [4000, 10, 700, 22222])
>>> sum(a)
26932
>>> a[1:3]
array('H', [10, 700])
Модуль collections предоставляет объект deque, который похож на список с более быстрыми добавлениями и удалениями с левой стороны, но более медленными поисками по середине. Эти объекты хорошо подходят для реализации очередей и поиска в ширину по дереву:
>>> from collections import deque
>>> d = deque(["task1", "task2", "task3"])
>>> d.append("task4")
>>> print("Handling", d.popleft())
Handling task1
unsearched = deque([starting_node])
def breadth_first_search(unsearched):
node = unsearched.popleft()
for m in gen_moves(node):
if is_goal(m):
return m
unsearched.append(m)
Помимо альтернативных реализаций списков, библиотека также предлагает другие инструменты, такие как модуль bisect с функциями для работы с отсортированными списками:
>>> import bisect >>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')] >>> bisect.insort(scores, (300, 'ruby')) >>> scores [(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]
Модуль heapq предоставляет функции для реализации куч на основе обычных списков. Элемент с наименьшим значением всегда сохраняется в позиции ноль. Это полезно для приложений, которые многократно обращаются к наименьшему элементу, но не хотят выполнять полную сортировку списка:
>>> from heapq import heapify, heappop, heappush >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] >>> heapify(data) # rearrange the list into heap order >>> heappush(data, -5) # add a new entry >>> [heappop(data) for i in range(3)] # fetch the three smallest entries [-5, 0, 1]
11.8. Десятичная арифметика с плавающей точкой
Модуль decimal предлагает тип данных Decimal для десятичной арифметики с плавающей точкой. По сравнению со встроенной реализацией двоичной плавающей точки float, класс особенно полезен для
- финансовых приложений и других применений, которые требуют точного десятичного представления,
- контроля точности,
- контроля округления для соответствия юридическим или нормативным требованиям,
- отслеживания значащих десятичных разрядов или
- приложений, где пользователь ожидает, что результаты будут соответствовать ручным расчетам.
Например, расчет налога в 5% от стоимости телефонного звонка в 70 центов дает разные результаты в десятичной и двоичной плавающей точке. Разница становится значительной, если результаты округляются до ближайшей копейки:
>>> from decimal import *
>>> round(Decimal('0.70') * Decimal('1.05'), 2)
Decimal('0.74')
>>> round(.70 * 1.05, 2)
0.73
Результат Decimal сохраняет конечный ноль, автоматически определяя значимость до четырёх знаков после запятой из множителей с двумя знаками после запятой. Десятичная арифметика воспроизводит вычисления, выполняемые вручную, и избегает проблем, которые могут возникнуть, когда двоичная плавающая точка не может точно представить десятичные величины.
Точное представление позволяет классу Decimal выполнять вычисления по модулю и проверки на равенство, которые неприменимы для двоичной плавающей точки:
>>> Decimal('1.00') % Decimal('.10')
Decimal('0.00')
>>> 1.00 % 0.10
0.09999999999999995
>>> sum([Decimal('0.1')]*10) == Decimal('1.0')
True
>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0
False
Модуль decimal обеспечивает арифметику с необходимой точностью:
>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/tutorial/stdlib2.html