11. Краткий обзор стандартной библиотеки — часть II

Этот второй тур охватывает более продвинутые модули, поддерживающие профессиональные программирование потребностей. Эти модули редко встречаются в небольших скриптах.

11.1. Форматирование вывода

Модуль reprlib предоставляет версию repr(), настроенную для сокращенное отображение больших или глубоко вложенных контейнеров:

>>> import reprlib
>>> reprlib.repr(set('supercalifragilisticexpialidocious'))
"{'a', 'c', 'd', 'e', 'f', 'g', ...}"

Модуль pprint обеспечивает более сложный контроль над печатью встроенные и пользовательские объекты, считываемые интерпретатор. Если результат длиннее одной строки, «print printer» добавляет разрывы строк и отступы для более четкого определения структуры данных:

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

>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
'English_United States.1252'
>>> conv = locale.localeconv()          # получить отображения соглашений
>>> 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.'

субклассes шаблона может указывать пользовательский разделитель. Например, партия утилита переименования для браузера фотографий может выбрать использование знаков процента для местозаполнители, такие как текущая дата, порядковый номер изображения или формат файла:

>>> 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):                      # показать первые 3 заголовка файла
    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     # перейти к следующему заголовку

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.')

Основная задача многопоточных приложений - координация потоков которые совместно используют данные или другие ресурсы. С этой целью модуль многопоточности предоставляет ряд примитивов синхронизации, включая блокировки, события, условия переменные и семафоры.

Несмотря на то, что эти инструменты являются мощными, незначительные ошибки проектирования могут привести к проблемам, их трудно воспроизвести. Таким образом, предпочтительным подходом к координации задач является чтобы сосредоточить весь доступ к ресурсу в одном потоке, а затем использовать 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. Новые фильтры могут выбрать различная маршрутизация на основе приоритета сообщения: const: „~ logging.DEBUG“, :const: „~ регистрация. ИНФОРМАЦИЯ“: константа: „~ регистрация. ПРЕДУПРЕЖДЕНИЕ“: константа: „~ регистрация. ОШИБКА“, и: константа: „~ регистрация. ОЧЕНЬ вАЖНЫЙ“.

Система регистрации может быть сконфигурирована непосредственно из 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)                   # создать ссылку
>>> d = weakref.WeakValueDictionary()
>>> d['primary'] = a            # не создает ссылку
>>> d['primary']                # получить объект, если он еще жив
10
>>> del a                       # удалить одну ссылку
>>> gc.collect()                # немедленно запустить сборку мусора
0
>>> d['primary']                # запись была автоматически удалена
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    d['primary']                # запись была автоматически удалена
  File "C:/python38/lib/weakref.py", line 46, in __getitem__
    o = self.data[key]()
KeyError: 'primary'

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

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

Модуль array обеспечивает deque() объект, который похож список, который хранит только однородные данные и более компактно. В следующем примере показан массив чисел, сохраненных в виде двоичных данных с двумя байтами без знака числа (typecode "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 предоставляет объект: класс: „~ 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 реализация двоичной плавающей точки, класс особенно полезна для

  • Финансовые приложения и другие виды использования, требующие точного десятичного представления,
  • контроль над точностью
  • control over rounding to meet legal or regulatory requirements,
  • tracking of significant decimal places, or
  • Приложения, в которых пользователь ожидает, что результаты будут соответствовать расчетам, выполненным вручную.

Например, расчет 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')