Краткий обзор стандартной библиотеки — часть 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.'
Подклассы Template могут задавать собственный разделитель. Например, утилита пакетного переименования файлов в программе для просмотра фотографий может использовать знаки процента в качестве заполнителей для текущей даты, порядкового номера изображения или формата файла:
>>> 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" обозначают соответственно двух- и четырёхбайтовые беззнаковые числа. Маркер "<" указывает, что используется стандартный размер и порядок байтов от младшего к старшему:
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 предоставляет средства для отслеживания объектов без создания ссылки на них. Когда объект больше не нужен, он автоматически удаляется из таблицы слабых ссылок, а для объектов 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. Средства для работы со списками
Многие задачи, связанные со структурами данных, можно решить с помощью встроенного типа list. Однако иногда нужны альтернативные реализации с другими компромиссами в производительности.
Модуль array предоставляет объект array, похожий на список, но хранящий данные только одного типа и занимающий меньше места. В следующем примере показан массив чисел, хранящихся в виде двухбайтовых беззнаковых двоичных чисел (код типа "H"), а не в обычном для списков виде — по 16 байт на каждый объект int Python:
>>> 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 выполнять вычисления по модулю и проверку равенства, для которых двоичная арифметика с плавающей точкой не подходит:
>>> 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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/tutorial/stdlib2.html