時間処理、乱数、便利なコンテナ型、反復処理について、順を追って学べるように整理しています。
参考として、次の記事も参照してください。
Pythonコーディングの基本91点:実務で使える概念とコード例
Pythonをより深く理解するための設計思想・内部構造・計算量
導読
- この文は、なぜ標準ライブラリを学ぶのか -> 時間を扱う -> 乱数を扱う -> コンテナ型を便利に使う -> 反復処理を効率化する、という流れで Python 標準ライブラリの基本を最初に押さえるためのメモです。
- 読み始めるときは、まず
time.localtime()、time.perf_counter()、random.seed()、random.choice()、Counter、deque、itertools.product()、zip_longest()、groupby()を優先して押さえると、実務でよく見る小さな処理が理解しやすくなります。
この文の章立ては、次のようになっています。
- なぜ Python 標準ライブラリを使うのか
- 標準ライブラリの位置付け: Python をインストールした時点で使える機能群であることを整理します。
- どんな場面で役立つか: 時刻処理、乱数、データ構造、反復処理で頻繁に登場することを確認します。
-
timeライブラリ- 現在時刻を取得する:
localtime()、gmtime()、ctime()を扱います。 - タイムスタンプと計測:
time()、perf_counter()、process_time()の違いを整理します。 - 表示形式を整える:
strftime()で日付文字列を作ります。 - 一時停止する:
sleep()の使いどころを確認します。
- 現在時刻を取得する:
-
randomライブラリ- 乱数の再現性:
seed()を使って同じ結果を再現します。 - 整数乱数と浮動小数点乱数:
randint()、randrange()、random()、uniform()をまとめます。 - シーケンス操作:
choice()、choices()、shuffle()、sample()の使い分けを確認します。 - 分布に従う乱数:
gauss()を使って正規分布の例を見ます。 - 応用例: ランダム配分と簡単な認証コード生成を扱います。
- 乱数の再現性:
-
collectionsライブラリ-
namedtuple: 名前付きタプルで読みやすいデータ構造を作ります。 -
Counter: 要素数の集計、上位件数、要素展開を扱います。 -
deque: 両端から高速に追加・削除できるキューを確認します。
-
-
itertoolsライブラリ- 積・順列・組合せ:
product()、permutations()、combinations()、combinations_with_replacement()を扱います。 - ラップ処理:
zip()、zip_longest()の違いを確認します。 - 無限イテレータ:
count()、cycle()、repeat()を安全に試します。 - その他の重要関数:
chain()、enumerate()、groupby()を扱います。
- 積・順列・組合せ:
なぜ Python 標準ライブラリを使うのか
この章では、標準ライブラリを学ぶ意味を先に整理します。外部ライブラリは非常に強力ですが、標準ライブラリだけで十分に解ける処理も多く、基本を知っているとコードが過剰に大きくなりにくくなります。
標準ライブラリとは何か
標準ライブラリは、Python 自身に最初から付属している機能群です。追加インストールなしで、時間処理、乱数、ファイル、データ構造、イテレータ、文字列処理などをすぐ使えます。
特に学習初期では、次のような場面で標準ライブラリの知識が役立ちます。
- 現在時刻や処理時間を測る。
- テスト用データや乱数を作る。
- 辞書やリストでは書きにくい集計を短く書く。
- 反復処理を簡潔かつ安全に書く。
どんな場面で役立つか
たとえば、処理時間の計測は time、ランダムサンプリングは random、頻度集計は collections.Counter、順列や組合せの列挙は itertools で自然に書けます。
import time
from collections import Counter
from itertools import product
from random import sample
start = time.perf_counter()
counts = Counter(["red", "blue", "red", "green", "blue", "blue"])
cards = list(product(["A", "K", "Q"], ["spade", "heart"]))
picked = sample(cards, k=2)
print(counts)
print(picked)
print("処理時間:", round(time.perf_counter() - start, 6), "秒")
time ライブラリ
この章では、時刻の取得、計測、整形、一時停止の基本をまとめます。機械学習やデータ分析のノートでも、処理時間の比較やログ出力の整形で頻繁に登場します。
現在時刻を取得する
time.localtime() はローカル時刻、time.gmtime() は UTC を返します。どちらも struct_time という形式で返るので、そのまま参照したり、別の関数へ渡したりできます。
import time
# 現在のローカル時刻と UTC を取得する
local_time = time.localtime()
utc_time = time.gmtime()
print("ローカル時刻:", local_time)
print("UTC:", utc_time)
# ctime() はローカル時刻を読みやすい文字列にして返す
print("文字列表現:", time.ctime())
localtime() と gmtime() の差はタイムゾーンによって変わります。日本標準時で動かしている場合は、通常 UTC より 9 時間進んだ値になります。
タイムスタンプと計時関数
Notebook では、time()、perf_counter()、process_time() を並べて比較していました。違いを押さえておくと、用途に応じて使い分けやすくなります。
-
time.time(): エポック秒を返します。実時刻に近い値です。 -
time.perf_counter(): 高精度な経過時間計測に向いています。sleep()も含みます。 -
time.process_time(): CPU が実際に使った時間を返します。sleep()は含みません。
import time
t1_start = time.time()
t2_start = time.perf_counter()
t3_start = time.process_time()
total = 0
for number in range(1_000_000):
total += number
# sleep() は待機時間なので process_time() には加算されない
time.sleep(2)
t1_end = time.time()
t2_end = time.perf_counter()
t3_end = time.process_time()
print("time() : {:.3f}秒".format(t1_end - t1_start))
print("perf_counter: {:.3f}秒".format(t2_end - t2_start))
print("process_time: {:.3f}秒".format(t3_end - t3_start))
処理速度を比較したいときは、通常 perf_counter() を使うと分かりやすいです。CPU の純粋な計算時間だけを見たいときは process_time() が役立ちます。
表示形式を整える
strftime() を使うと、日付や時刻を好きな文字列形式へ変換できます。ログファイル名やレポートの見出しでよく使います。
import time
local_time = time.localtime()
# 年-月-日 曜日 時:分:秒 の形式へ整える
formatted = time.strftime("%Y-%m-%d %A %H:%M:%S", local_time)
print(formatted)
代表的な書式指定子は次のとおりです。
-
%Y: 西暦 4 桁 -
%m: 月 2 桁 -
%d: 日 2 桁 -
%H: 時 2 桁 -
%M: 分 2 桁 -
%S: 秒 2 桁 -
%A: 曜日の完全表記
一時停止する
sleep() は、指定秒数だけ処理を止めます。API の呼び出し間隔を空けたいときや、デモで動きをゆっくり見せたいときに便利です。
import time
print("3 秒待機します")
time.sleep(3)
print("再開しました")
random ライブラリ
この章では、再現可能な乱数、整数乱数、浮動小数点乱数、サンプリング、確率分布の基本を整理します。検証用データを作るとき、ゲームやシミュレーションを書くとき、簡単な抽選処理を作るときに特に便利です。
乱数の再現性を seed() で固定する
同じシード値を設定すると、同じ乱数列を再現できます。Notebook でも、seed(10) を使って 2 回同じ値が出ることを確認していました。
import random
# 同じ seed を使うと同じ乱数列を再現できる
random.seed(10)
print(random.random())
random.seed(10)
print(random.random())
# seed を指定しなければ、その時点の状態から次の乱数が生成される
print(random.random())
再現性が必要な学習コードやテストコードでは、最初に seed() を設定しておくと挙動を追いやすくなります。
整数乱数を作る
整数乱数では、randint() と randrange() を使い分けます。閉区間なのか半開区間なのかが違うので、そこを意識して選びます。
from random import randint, randrange
# randint(a, b) は a 以上 b 以下の整数を返す
numbers = [randint(1, 10) for _ in range(10)]
print(numbers)
# randrange(stop) は range(stop) と同じ範囲から選ぶ
numbers = [randrange(10) for _ in range(10)]
print(numbers)
# randrange(start, stop, step) は step を考慮して選ぶ
numbers = [randrange(0, 10, 2) for _ in range(10)]
print(numbers)
浮動小数点乱数を作る
random() は 0.0 以上 1.0 未満、uniform(a, b) は指定範囲内の浮動小数点数を返します。
from random import random, uniform
# 0.0 以上 1.0 未満の乱数
numbers = [random() for _ in range(10)]
print(numbers)
# 指定範囲の浮動小数点乱数
numbers = [uniform(2.1, 3.5) for _ in range(10)]
print(numbers)
シーケンスからランダムに選ぶ
choice()、choices()、shuffle()、sample() は、それぞれ用途が少し異なります。
-
choice(seq): 1 つだけ選ぶ -
choices(seq, weights=None, k=n): 重み付きで複数回抽出する。重複あり -
shuffle(seq): 元のリスト自体を並べ替える -
sample(population, k=n): 重複なしで複数個取り出す
from random import choice, choices, sample, shuffle
print(choice(["win", "lose", "draw"]))
print(choice("python"))
# 同じ要素が重複して選ばれることがある
print(choices(["win", "lose", "draw"], k=5))
# 重み付き抽選もできる
print(choices(["win", "lose", "draw"], weights=[4, 4, 2], k=10))
cards = ["one", "two", "three", "four"]
shuffle(cards) # 元のリスト自体を書き換える
print(cards)
print(sample([10, 20, 30, 40, 50], k=3))
正規分布に従う乱数を作る
gauss(mean, std) を使うと、正規分布に従う乱数を 1 つずつ生成できます。Notebook では大量に作ってヒストグラムを確認していました。
from random import gauss
# 平均 0、標準偏差 1 の正規乱数を 1 つ作る
number = gauss(0, 1)
print(number)
import matplotlib.pyplot as plt
from random import gauss
# たくさん生成すると、山形の分布になりやすい
samples = [gauss(0, 1) for _ in range(100000)]
plt.hist(samples, bins=200)
plt.show()
応用例 1: お年玉をランダムに配分する
Notebook ではランダムに金額を分配する例がありました。日本語の文脈では、お年玉の配分例に置き換えると自然です。
import random
def otoshidama(total_yen, num_people):
"""合計金額を num_people 人へランダムに配分する"""
remaining = total_yen
for index in range(1, num_people):
# 残り人数を考慮しつつ、期待値が大きく崩れすぎない範囲で乱数を作る
amount = random.uniform(1, remaining / (num_people - index + 1) * 2)
remaining -= amount
print("{} 人目の金額: {:.0f} 円".format(index, amount))
else:
print("{} 人目の金額: {:.0f} 円".format(num_people, remaining))
otoshidama(1000, 5)
何度も実行すると、各順番の平均は極端には偏りにくいことも確認できます。
import random
def otoshidama_list(total_yen, num_people):
"""配分結果をリストで返す"""
remaining = total_yen
amounts = []
for index in range(1, num_people):
amount = round(random.uniform(1, remaining / (num_people - index + 1) * 2))
amounts.append(amount)
remaining -= amount
else:
amounts.append(round(remaining))
return amounts
results = [otoshidama_list(1000, 5) for _ in range(10000)]
# 各位置の平均金額を計算する
averages = [sum(result[i] for result in results) / len(results) for i in range(5)]
print(averages)
応用例 2: 4 文字の認証コードを作る
英数字からランダムに 4 文字を選ぶ例です。Notebook では string ライブラリも併用していました。
import random
import string
print(string.digits)
print(string.ascii_letters)
characters = string.digits + string.ascii_letters
verification_code = random.sample(characters, 4)
print(verification_code)
print("".join(verification_code))
collections ライブラリ
この章では、通常のタプル・辞書・リストだけでは少し書きにくい場面を助けてくれる collections の基本をまとめます。
namedtuple: 名前付きタプル
単なるタプル (1, 2) だけを見ると、その 2 つの値が何を表すのか分かりにくいことがあります。namedtuple を使うと、属性名付きで読みやすいタプルを作れます。
namedtuple は、collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None) の形で定義します。
from collections import namedtuple
# 普通のタプルだけでは、何を表す値かが分かりにくい
point_tuple = (1, 2)
print(point_tuple)
# 名前付きタプルを定義する
Point = namedtuple("Point", ["x", "y"])
point = Point(1, y=2)
print(point)
print(point.x)
print(point.y)
namedtuple は読みやすいだけでなく、タプルの性質もそのまま持っています。
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
point = Point(1, 2)
# インデックスアクセスやアンパックもできる
print(point[0])
print(point[1])
x, y = point
print(x)
print(y)
# 実際に tuple の子クラスであることも確認できる
print(isinstance(point, tuple))
namedtuple の応用: トランプを表す
Notebook では、トランプのカードを rank と suit を持つ名前付きタプルで表していました。データ構造を読みやすく表現する良い例です。
from collections import namedtuple
from random import choice, sample, shuffle
Card = namedtuple("Card", ["rank", "suit"])
ranks = [str(number) for number in range(2, 11)] + list("JQKA")
suits = "spades diamonds clubs hearts".split()
cards = [Card(rank, suit) for rank in ranks for suit in suits]
print(cards[:5])
# シャッフルしてから 1 枚または複数枚を引く
shuffle(cards)
print(choice(cards))
print(sample(cards, k=5))
Counter: 要素数を数える
Counter は、文字列やリストの要素が何回出てきたかをすぐ数えられる便利な辞書型です。Notebook では文字列と色リストの例を扱っていました。
from collections import Counter
text = "すもももももももものうち"
colors = ["red", "blue", "red", "green", "blue", "blue"]
count_text = Counter(text)
count_colors = Counter(colors)
print(count_text)
print(count_colors)
Counter は辞書の子クラスなので、辞書のように扱えます。
from collections import Counter
print(isinstance(Counter(), dict))
頻度上位を見たいときは most_common()、要素を元の個数分だけ展開したいときは elements() を使います。
from collections import Counter
counter = Counter(["red", "blue", "red", "green", "blue", "blue"])
# 頻度の高い要素を順に返す
print(counter.most_common(2))
# 要素を個数分だけ展開する
print(list(counter.elements()))
加算や減算のような操作もできます。
from collections import Counter
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
print(c + d)
print(c - d)
Counter の応用: 10 以上のカードの比率を調べる
トランプ 1 組では、10・J・Q・K の 4 ランクが各 4 枚ずつあるので、「10 以上のカード」は合計 16 枚あります。Notebook では、その比率をサンプリングで確認していました。
from collections import Counter
from random import sample
# 10 以上のカード 16 枚、その他のカード 36 枚とみなす
cards = collections.Counter(high_cards=16, low_cards=36)
seen = sample(list(cards.elements()), k=10)
print(seen)
print(seen.count("high_cards") / 10)
deque: 両端キュー
リストは末尾への追加・削除は速い一方で、先頭側への挿入や削除は要素の移動が発生するため遅くなりやすいです。両端を頻繁に操作したいなら deque が向いています。
from collections import deque
d = deque("cde")
print(d)
# 右端へ追加する
d.append("f")
d.append("g")
# 左端へ追加する
d.appendleft("b")
d.appendleft("a")
print(d)
# 右端と左端から削除する
d.pop()
d.popleft()
print(d)
キューや履歴管理、スライディングウィンドウのような処理では deque が非常に便利です。
itertools ライブラリ
この章では、積・順列・組合せ、複数系列のまとめ処理、無限イテレータ、グループ化などを整理します。itertools を知っていると、二重三重のループを短く書けるようになります。
積・順列・組合せを作る
product() は直積、permutations() は順列、combinations() は重複なしの組合せ、combinations_with_replacement() は重複ありの組合せです。
import itertools
# 直積
for item in itertools.product("ABC", "01"):
print(item)
import itertools
# 同じ集合を繰り返して直積を作る
for item in itertools.product("ABC", repeat=3):
print(item)
import itertools
# 順列
for item in itertools.permutations("ABCD", 3):
print(item)
import itertools
# 組合せ
for item in itertools.combinations("ABCD", 2):
print(item)
import itertools
# 重複ありの組合せ
for item in itertools.combinations_with_replacement("ABC", 2):
print(item)
これらは、探索条件の列挙、パラメータ組合せの確認、小さな全探索の実験などでよく使います。
zip() と zip_longest()
複数のシーケンスを同時に処理したいときは zip() が基本です。長さが違うと短い方で止まるので、そのままでは最後まで見たいときに向きません。そういうときは zip_longest() を使います。
# zip は Python の組み込み関数
for item in zip("ABC", "012", "xyz"):
print(item)
# 長さが違う場合は、短い方に合わせて終了する
for item in zip("ABC", [0, 1, 2, 3, 4, 5]):
print(item)
import itertools
# zip_longest は長い方に合わせて進み、足りない部分は None になる
for item in itertools.zip_longest("ABC", "012345"):
print(item)
import itertools
# fillvalue を指定すると不足分を任意の値で埋められる
for item in itertools.zip_longest("ABC", "012345", fillvalue="?"):
print(item)
無限イテレータを安全に使う
count()、cycle()、repeat() は無限に値を返し続ける可能性があるので、そのまま for で回すと止まりません。Notebook では概念的に出力だけ示していましたが、実際に試すときは itertools.islice() を併用すると安全です。
import itertools
# count(start=10) から最初の 5 個だけ取り出す
print(list(itertools.islice(itertools.count(10), 5)))
import itertools
# cycle() は同じ並びを繰り返す
print(list(itertools.islice(itertools.cycle("ABC"), 8)))
import itertools
# repeat() は同じ値を繰り返す
for item in itertools.repeat(10, 3):
print(item)
その他の重要な関数
chain()
複数の反復可能オブジェクトを 1 本につなげたいときに使います。
import itertools
for item in itertools.chain("ABC", [1, 2, 3]):
print(item)
enumerate()
インデックスと値を同時に取り出したいときに使います。これは itertools ではなく Python 組み込みですが、itertools の節と一緒に覚えると便利です。
for item in enumerate("Python", start=1):
print(item)
groupby()
groupby() は、連続して並んでいる同じキーの要素をまとめます。そのため、一般には先に並べ替えてから使うと分かりやすくなります。
import itertools
for key, group in itertools.groupby("AAAABBBCCDAABBB"):
print(key, list(group))
長さでまとめる例です。
import itertools
animals = ["duck", "eagle", "rat", "giraffe", "bear", "bat", "dolphin", "shark", "lion"]
animals.sort(key=len)
print(animals)
for key, group in itertools.groupby(animals, key=len):
print(key, list(group))
先頭文字でまとめる例です。
import itertools
animals = ["duck", "eagle", "rat", "giraffe", "bear", "bat", "dolphin", "shark", "lion"]
animals.sort(key=lambda value: value[0])
print(animals)
for key, group in itertools.groupby(animals, key=lambda value: value[0]):
print(key, list(group))
groupby() は「同じ値を全部まとめる」より、「連続したかたまりをまとめる」と理解しておくのが重要です。
まとめ
Python 標準ライブラリを最初に学ぶときは、関数名をばらばらに覚えるよりも、次の流れでつなげて覚えると定着しやすくなります。
-
timeで現在時刻、処理時間、待機の基本を押さえる。 -
randomで乱数生成、サンプリング、簡単なシミュレーションを扱えるようにする。 -
collectionsでnamedtuple、Counter、dequeを使い分けられるようにする。 -
itertoolsで積・順列・組合せ・グループ化を短く安全に書けるようにする。
このあたりを理解しておくと、NumPy や Pandas を使わない小さな処理でも、標準ライブラリだけでかなり読みやすく書けるようになります。また、外部ライブラリのノートを読むときにも、「これは単なる前処理なのか」「標準機能だけで書けるのか」を判断しやすくなります。