Spec-Zone.ru › Python 3.12

Краткий обзор стандартной библиотеки — Часть 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 предоставляет прямой способ форматирования чисел с разделителями группировки:

>>> 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 предоставляет инструменты для отслеживания объектов без создания ссылки. Когда объект больше не нужен, он автоматически удаляется из таблицы 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:/python312/lib/weakref.py", line 46, in __getitem__
    o = self.data[key]()
KeyError: 'primary'
END_OF_DOCUMENT_MARKER

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.12/tutorial/stdlib2.html

Spec-Zone.ru

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