Pythonの標準ライブラリ:後半（Python学習メモ⑨）

## 出力整形

`reprlib`, `pprint`, `textwrap`, `locale`など


```python

import reprlib
import pprint
import textwrap
import locale

# reprlibはrepr()の別バージョンで、巨大なコンテナお部ジェクトを省略して表示する
charset = reprlib.repr(set('supercalifragilisticexpialidocious'))
print(charset)
# {'a', 'c', 'd', 'e', 'f', 'g', ...}

# pprintはデータ構造を分かりやすく表示してくれる
t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', 'yellow'], 'blue']]]
pprint.pprint(t, width=30)
# [[[['black', 'cyan'],
#    'white',
#    ['green', 'red']],
#   [['magenta', 'yellow'],
#    'blue']]]

# textwrap.fill()は各段落を指定の幅におさまるように折り返してくれる
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.setlocale(locale.LC_ALL, 'English_United States.1252')
conv = locale.localeconv()
x = 1234567.8
print(locale.format("%d", x, grouping=True))
# 1,234,567
print(locale.format_string("%s%.*f", (conv['currency_symbol'], conv['frac_digits'], x), grouping=True))
# $1,234,567.80
```

## テンプレート
ポイント

* `Template`モジュールを使うと置き換え記号($)を使ったテンプレート文字列を実装することができる
* $の後に英数字、アンダースコアを用いた数字を続けられる
* \$は\$でエスケープ可能
* $のあとに｛｝を続けるとスペースを入れなくても英数字、アンダースコアを続けることが可能

```python
from string import Template
t = Template('${village}folk send $$10 to $cause.')
sentence = (t.substitute(village='Nottingham', cause='the ditch func'))

print(sentence)
# Nottinghamfolk send $10 to the ditch func.


# substituteメソッドはプレースホルダをディクショナリか
# キーワード引数を渡さないとKeyErrorとなるので、
# safe_substituteメソッドの方が安全な場合がある
# データがなければプレースホルダはそのまま出力される
t = Template('Return the $item to $owner.')
d = dict(item='unladen swallow')
sentence = t.safe_substitute(d)

print(sentence)
# Return the unladen swallow to $owner.
```

Templateのサブクラスでは$の代わりに、区切り文字を変えることもできる

```python:ファイル目の変更
import time, os.path
from string import Template

photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']

class BatchRename(Template):
    delimiter = '%'

fmt = input('どのようにリネームしますか (%d- 日付 %n- 番号 %f- 形式): ')
# どのようにリネームしますか (%d- 日付 %n- 番号 %f- 形式): 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
```


## バイナリデータレコードの処理

`struct`モジュールの`pack()`と`unpack()`を利用して、可変長のバイナリレコードを処理することが出来る

```python:zipファイルの各ヘッダ情報にループをかける例

import struct

# rb: バイナリでread
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 # 次のヘッダまでスキップ
```

## マルチスレッド
ポイント

* `threading`モジュールを利用することで、メインプログラムを走らせたままバックグラウンド処理ができる

```python
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('メインプログラムは実行中です')

background.join() # バックグラウンドタスクの終了を待つ

print('メインプログラムはバックグラウンド処理の終了まで待っていました')
```


* マルチスレッドアプリケーションにおけるタスク協調においては単一のメインスレッドにリソースへのアクセスを集中させておいて、ほかのスレッドからのリクエストは`queue`モジュールを使ってメインスレッドに食わせる、というアプローチがよい
* スレッド間通信にはQueueオブジェクトを使うと設計しやすく可読性も上がる

## ログ取り
ポイント

* `logging`モジュールでは柔軟なログ記録システム
* デフォルトではDEBUGメッセージとINFOメッセージが抑制されている
* デフォルトでは出力先は標準エラー出力になっている
* 出力オプションとしてはほかにeメール、データグラム、ソケット、HTTPサーバへの転送がある
* メッセージの優先度毎に出力先を選ぶこともできる

```python
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

```

## 弱参照

* `weakref`を用いると参照を生成せずにオブジェクトを追跡することができる
* 不要になったオブジェクトは自動的に弱参照表から除かれ、弱参照オブジェクトへのコールバックが起きる

```python
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 # 参照を生成しない
print(d['primary']) # オブジェクトが生きていれば取ってくる
10

del a # 参照を削除
gc.collect() # ガベージコレクションを実行

d['primary'] # エントリは自動的に削除されている
# Traceback (most recent call last):
#   File ".\weakref_sample.py", line 17, in <module>
#     d['primary'] # エントリは自動的に削除されている
#   File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.1520.0_x64__qbz5n2kfra8p0\lib\weakref.py", line 137, in __getitem__      
#     o = self.data[key]()
# KeyError: 'primary'
```

## リスト操作のツール

* `array`モジュールは、同室のデータのみを収めるリスト類似のオブジェクト`array`を提供する

```python

from array import array

a = array('H', [4000, 10, 700, 22222])
print(sum(a))
# 26932

print(a[1:3])
# array('H', [10, 700])
```

* `collections`モジュールは左端部への`append`や`pop`はリストより高速だが、中央部の参照は低速である`deque`オブジェクトをもたらす
* キューや幅優先ツリー検索を実装するときに便利


```python

from collections import deque
d = deque(["task1", "task2", "task3"])
d.append("task4")
print("Handling", d.popleft())

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`モジュールなども備えている
* `heapq`モジュールは通常のリストをベースにヒープを実装する関数を提供する

## 10進数の浮動小数点計算

* `decimal`モジュールは浮動小数点10進数で計算するためのデータ型`Decimal`を提供する
* `float`型より厳密な計算が可能