第11章 標準ライブラリめぐり PartII(40問中1問)
Python3基礎試験:標準ライブラリ その2
第11章は、第10章の続きとして、小さなスクリプトではあまり使わないが業務の開発で役に立つモジュールを見て回る章です。このページでは、公式チュートリアルの記載と、Python 3.9.6 で実行した結果を並べて確かめます。覚えるのは、logging の既定の出力、Template の2つのメソッドの違い、deque と heapq の動き、Decimal と float の違いの4つです。
このページは試験の学習用の整理です。公式の問題は転載せず、例は公式チュートリアルの記載をもとにしています。
この記事の目次
公式情報の確認
試験日、料金、申込条件、試験方式など変動する情報は、公式情報を最終確認してください。 当サイトの一次情報確認日: 2026-09-18。
試験の出題範囲・実施要項は変動するため、受験前に公式情報を確認してください。
結論
- logging は、標準の設定では info() と debug() の出力が抑制され、warning 以上が標準エラーに送られる
- Template の substitute() は値が足りないと KeyError、safe_substitute() はプレースホルダを残して出力する。$$ で $ そのものを書ける
- weakref は参照を作らずに追跡するので、最後の通常の参照を消すと、弱参照の辞書から項目が自動で消える
- deque は追加と左端からの取り出し(popleft())が速く、heapq は位置0に最小値が来る。bisect.insort() はソート済みのまま挿入する
- Decimal は10進数を厳密に扱う。Decimal('0.70') * Decimal('1.05') を丸めると 0.74、float の 0.70 * 1.05 は 0.73 になる
| やりたいこと | モジュール | ひとこと |
|---|---|---|
| 大きなコンテナを省略して表示 | reprlib | repr() の省略版 |
| 入れ子のデータを改行とインデントで表示 | pprint | width で幅を指定 |
| 段落を指定幅に収める | textwrap | fill() は改行入りの1つの文字列 |
| $ のプレースホルダで文字列を組み立てる | string.Template | substitute・safe_substitute |
| バイナリのレコードを pack・unpack | struct | '<' はリトルエンディアン |
| バックグラウンドで処理を動かす | threading | 調整は queue の利用を推奨 |
| ログを記録する | logging | 既定では warning 以上が標準エラーへ |
| 参照を作らずにオブジェクトを追跡 | weakref | 作成コストの大きいもののキャッシュ |
| 同種のデータをコンパクトに格納 | array | タイプコード H は符号なし2バイト |
| 両端の追加・取り出しが速いリスト | collections.deque | popleft() が速い |
| ソート済みリストへの挿入 | bisect | insort() |
| 最小値をすぐ取り出す | heapq | 位置0が最小値 |
| 10進数を厳密に計算 | decimal | Decimal('0.70') のように文字列で作る |
出力の整形とTemplate
公式チュートリアルによると、reprlib は大きなコンテナや深くネストしたコンテナを省略して表示する repr() を、pprint は改行とインデントを加えて構造をわかりやすく表示する手段を、textwrap は段落を指定した幅に収める手段を提供します。Python 3.9.6 で実行すると、次の出力になりました。
reprlib.repr(set('supercalifragilisticexpialidocious'))
{'a', 'c', 'd', 'e', 'f', 'g', ...}
pprint.pprint(t, width=30)
[[[['black', 'cyan'],
'white',
['green', 'red']],
[['magenta', 'yellow'],
'blue']]]string モジュールの Template クラスは、$ と識別子からなるプレースホルダを使います。後ろに英数字が続くときは ${village} のように {} で囲み、$$ と書くと $ 自体になります。
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.'値が足りないときの違いが試験で問われやすい点です。Python 3.9.6 で、owner を渡さずに試しました。
t = Template('Return the $item to $owner.')
d = dict(item='unladen swallow')
t.substitute(d)
KeyError: 'owner'
t.safe_substitute(d)
'Return the unladen swallow to $owner.'substitute() は欠けた値で KeyError、safe_substitute() はプレースホルダを残します。公式チュートリアルは、ユーザーが入力するデータが不完全になりうるメールマージのような用途では safe_substitute() が適していると説明しています。区切り文字はデフォルトで $ ですが、Template のサブクラスで変更できます。
struct モジュールは、pack() と unpack() でバイナリのレコードを扱います。パック符号 H と I は、それぞれ2バイトと4バイトの符号なし整数で、先頭の < はリトルエンディアンで標準サイズを表します。threading は高水準のモジュールで、バックグラウンドで処理を動かせます。スレッド間の調整には、あるリソースへのアクセスを1つのスレッドに集め、queue モジュールで他のスレッドからの要求を送る方法が推奨されています。
loggingと弱参照
logging モジュールは、柔軟なログ記録システムです。最も簡単な使い方では、ログメッセージをファイルや sys.stderr に送ります。debug・info・warning・error・critical を順に呼んで、Python 3.9.6 で実行しました。
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標準の設定では debug と info は出ず、warning・error・critical だけが標準エラーに出ます。優先度は DEBUG・INFO・WARNING・ERROR・CRITICAL で、フィルタを作ると優先度ごとに送信先を変えられます。設定は Python から直接行うことも、ユーザーが編集できる設定ファイルで行うこともできます。
Python は、ほとんどのオブジェクトを参照カウント方式で管理し、ガベージコレクションで循環参照を取り除きます。オブジェクトを追跡するだけで参照が作られ、解放されなくなる問題を避けるのが weakref モジュールです。典型的な用途は、作成コストの大きいオブジェクトのキャッシュです。
d = weakref.WeakValueDictionary()
d['primary'] = a # 参照を作らない
d['primary']
10
del a # 唯一の参照を削除
gc.collect()
d['primary']
KeyError: 'primary'弱参照の辞書は、元のオブジェクトが消えると項目も自動で消えます。通常の辞書に入れると参照が残るので、オブジェクトは解放されません。
リストを操作するツール
多くの場面では組み込みのリストで足ります。性能のトレードオフが違う別の実装が必要なときのために、標準ライブラリには array・deque・bisect・heapq があります。Python 3.9.6 の実行結果は次のとおりです。
a = array('H', [4000, 10, 700, 22222])
sum(a) -> 26932
a[1:3] -> array('H', [10, 700])
a.itemsize -> 2
d = deque(['task1', 'task2', 'task3'])
d.append('task4')
d.popleft() -> 'task1'
list(d) -> ['task2', 'task3', 'task4']
bisect.insort(scores, (300, 'ruby'))
[(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]
heapify(data); heappush(data, -5)
[heappop(data) for i in range(3)] -> [-5, 0, 1]array は同種のデータをよりコンパクトに格納します。タイプコード H は符号なしの2バイト整数で、公式チュートリアルが「通常1要素あたり16バイト使う」と書く Python の int のリストより、小さく格納できます。deque は追加と左端からの取り出しが速い一方で、中間の値の参照は遅くなります。キューや幅優先探索に向いています。bisect はソート済みのまま挿入し、heapq は位置0に最小値が来ます。heapq は、毎回ソートし直さずに最小の要素へ繰り返しアクセスするときに便利です。
Decimalとfloatの違い
decimal モジュールの Decimal は、10進浮動小数点の演算を提供します。公式チュートリアルは、財務アプリケーションなど正確な10進表記が必要な場合、精度や丸めの制御が必要な場合、手計算と同じ結果を期待する場合に便利だと説明しています。Python 3.9.6 で、70セントに5%の税を加える計算などを比べました。
round(Decimal('0.70') * Decimal('1.05'), 2) -> Decimal('0.74')
round(.70 * 1.05, 2) -> 0.73
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 を10回足した float == 1.0 -> False
getcontext().prec = 36
Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')Decimal は10進数を厳密に表せるので、剰余も等値の判定も手計算どおりになります。float は2進数で表すため、0.1 のような値が正確に表せず、足し算を重ねると誤差が出ます。getcontext().prec で、必要な桁数まで精度を上げられます。Decimal は文字列で作るのが基本です。
Python3エンジニア認定基礎試験の学習に使う書籍(Amazon)
試験範囲を通して読むときの1冊です。読んだ章は、このサイトの章別問題で確かめられます。
よくある質問
第11章の標準ライブラリめぐり PartII は、何問出ますか?
このサイトが整理した公式の章別出題数では、40問中1問(2.5%)です。出題数は少ない章なので、logging の既定の出力、Template の substitute と safe_substitute の違い、deque・heapq の動き、Decimal と float の違いを優先して確認します。
logging.info() を呼んでも何も表示されないのはなぜですか?
標準の設定では、info() と debug() の出力は抑制されるためです。公式チュートリアルによると、出力は標準エラーに送られます。Python 3.9.6 で debug・info・warning・error・critical を順に呼ぶと、WARNING・ERROR・CRITICAL の3行だけが表示されました。
Template の substitute と safe_substitute は何が違いますか?
値が足りないときの動きが違います。substitute() は辞書やキーワード引数に値が無いと KeyError を送出し、safe_substitute() はプレースホルダをそのまま残して出力します。Python 3.9.6 で owner を渡さずに試すと、substitute() は KeyError: 'owner'、safe_substitute() は 'Return the unladen swallow to $owner.' になりました。
Decimal は float と何が違いますか?
Decimal は10進数の値を厳密に扱えます。float は2進数で表すので、0.1 を10回足しても 1.0 と等しくなりません。Python 3.9.6 で試すと、Decimal('0.1') を10回足した合計は Decimal('1.0') と等しく(True)、float の 0.1 を10回足した合計は 1.0 と等しくありませんでした(False)。
