Spec-Zone.ru › Python 3.9

Краткий обзор стандартной библиотеки — Часть 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 имеет доступ к базе данных форматов данных, специфичных для культуры. Атрибут grouping функции locale.format предоставляет прямой способ форматирования чисел с разделителями группировки:

>>> 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("%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:/python39/lib/weakref.py", line 46, in __getitem__
    o = self.data[key]()
KeyError: 'primary'

11.7. Инструменты для работы со списками

Многие потребности в структурах данных могут быть удовлетворены встроенным типом списка. Однако иногда необходимо альтернативные реализации с различными компромиссами в производительности.

Модуль 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('1.00') % Decimal('.10')
Decimal('0.00')
>>> 1.00 % 0.10
0.09999999999999995

>>> sum([Decimal('0.1')]*10) == Decimal('1.0')
True
>>> sum([0.1]*10) == 1.0
False

Модуль decimal предоставляет арифметику с необходимой точностью:

>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/tutorial/stdlib2.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API