まえがき
文字列やコレクションに関するメソッドについて一度整理する。講義というより、私が忘れつつある内容を一度整理するというのが主目的。(ただし、この記事で必要なメソッドは網羅できるようにするつもりである)
このあたりの話は、「使い方」が重要なので、いちいち小難しい話を並べるよりも実例を多く示す方がよいと考え、そのスタイルで記述している。
文字列の操作
文字列の操作するための関数/メソッドについて順に扱う。
文字列の長さを取得する
文字列の長さ(文字列)を取得するには、組み込み関数 len を取得する。
# 文字列
print(len("Python")) # 6
# リスト
print(len([10, 20, 30])) # 3
# 辞書
print(len({"a": 1, "b": 2})) # 2
# セット
print(len({1, 2, 3, 3})) # 3(重複は除外)
文字列を大文字 ⇆ 小文字で変換する
| メソッド | 変換内容 | 特徴 |
|---|---|---|
upper() |
全部を大文字 | 英字以外はそのまま |
lower() |
全部を小文字 | 英字以外はそのまま |
capitalize() |
先頭1文字を大文字、それ以外を小文字 | 文頭の整形に便利 |
title() |
単語ごとに先頭を大文字、それ以外小文字 | タイトル表記向き |
swapcase() |
大文字⇔小文字を反転 | スタイル変換 |
casefold() |
小文字化(国際化対応でより強力) | 文字比較に推奨 |
# 文字列の大文字・小文字変換メソッドまとめ
s = "PyThOn World Straße"
# すべて大文字
print("upper(): ", s.upper())
# upper(): PYTHON WORLD STRASSE
# すべて小文字
print("lower(): ", s.lower())
# lower(): python world straße
# 先頭1文字だけ大文字、それ以降は小文字
print("capitalize():", s.capitalize())
# capitalize(): Python world straße
# 単語ごとに先頭を大文字、それ以外は小文字
print("title(): ", s.title())
# title(): Python World Straße
# 大文字⇔小文字を反転
print("swapcase(): ", s.swapcase())
# swapcase(): pYtHoN wORLD sTRASSE
# 国際化対応の強力な小文字化(ß → ss など)
print("casefold(): ", s.casefold())
# casefold(): python world strasse
# 応用:大文字小文字を区別しない比較
s1 = "Straße"
s2 = "STRASSE"
print("lower比較: ", s1.lower() == s2.lower())
# lower比較: False
print("casefold比較:", s1.casefold() == s2.casefold())
# casefold比較: True
部分文字列を取得する
文字列から部分的な文字列を取り出すなら、インデックス/スライスを利用する。
インデックスでは文字列の特定の位置にある1文字を取得し、スライスで部分文字列を取り出す。
# indexの構文 → s[index]
s = "Python"
print(s[0]) # 'P'(先頭)
print(s[1]) # 'y'
print(s[-1]) # 'n'(末尾)
print(s[-2]) # 'o'
# sliceの構文 → s[start:end:step]
print(s[0:2]) # 'Py'(0~1文字目)
print(s[2:]) # 'thon'(2文字目以降)
print(s[:4]) # 'Pyth'(先頭から3文字目まで)
print(s[::2]) # 'Pto'(2文字ごとに抽出)
print(s[::-1]) # 'nohtyP'(逆順)
| 操作 | 構文 | 例 | 出力 |
|---|---|---|---|
| 特定の文字を取得 | s[i] |
"Python"[0] |
'P' |
| 末尾から取得 | s[-i] |
"Python"[-1] |
'n' |
| 部分文字列 | s[start:end] |
"Python"[0:2] |
'Py' |
| ステップ指定 | s[start:end:step] |
"Python"[::2] |
'Pto' |
| 逆順 | s[::-1] |
"Python"[::-1] |
'nohtyP' |
文字の種類を判別する
| メソッド | 判定内容 | 使用例 |
|---|---|---|
isalpha() |
文字列が アルファベット(Unicodeの文字) のみか | "abc".isalpha() → True |
isalnum() |
文字列が 英数字(アルファベット+数字) のみか | "abc123".isalnum() → True |
isdigit() |
文字列が 数字(0〜9) のみか | "123".isdigit() → True |
isdecimal() |
文字列が 10進数字 のみか(小数・ローマ数字などは除外) | "123".isdecimal() → True |
isnumeric() |
文字列が 数値文字 のみか(分数・ローマ数字なども含む) | "Ⅲ".isnumeric() → True |
islower() |
文字列が すべて小文字 か | "python".islower() → True |
isupper() |
文字列が すべて大文字 か | "PYTHON".isupper() → True |
istitle() |
単語ごとに先頭大文字・それ以外小文字 になっているか | "Python Book".istitle() → True |
isspace() |
文字列が 空白(スペース, 改行, タブなど) のみか | " ".isspace() → True |
isprintable() |
文字列が 印字可能な文字のみ か(制御文字を含まない) | "Hello".isprintable() → True |
isascii() |
文字列が ASCII文字(0〜127の範囲) のみか | "ABC123".isascii() → True |
s_list = ["Python", "123", "Ⅲ", "hello world", "HELLO", " ", "Hello World"]
for s in s_list:
print(f"文字列: {s}")
print(" isalpha():", s.isalpha()) # アルファベットのみか
print(" isalnum():", s.isalnum()) # 英数字のみか
print(" isdigit():", s.isdigit()) # 数字か
print(" isdecimal():", s.isdecimal()) # 10進数字か
print(" isnumeric():", s.isnumeric()) # 数値文字か
print(" islower():", s.islower()) # 小文字か
print(" isupper():", s.isupper()) # 大文字か
print(" istitle():", s.istitle()) # タイトルケースか
print(" isspace():", s.isspace()) # 空白のみか
print(" isprintable():", s.isprintable()) # 印字可能か
print(" isascii():", s.isascii()) # ASCIIか
"""
=== 実行結果 ===
文字列: Python
isalpha(): True
isalnum(): True
isdigit(): False
isdecimal(): False
isnumeric(): False
islower(): False
isupper(): False
istitle(): True
isspace(): False
isprintable(): True
isascii(): True
文字列: 123
isalpha(): False
isalnum(): True
isdigit(): True
isdecimal(): True
isnumeric(): True
islower(): False
isupper(): False
istitle(): False
isspace(): False
isprintable(): True
isascii(): True
文字列: Ⅲ
isalpha(): False
isalnum(): True
isdigit(): False
isdecimal(): False
isnumeric(): True
islower(): False
isupper(): False
istitle(): False
isspace(): False
isprintable(): True
isascii(): False
文字列: hello world
isalpha(): False
isalnum(): False
isdigit(): False
isdecimal(): False
isnumeric(): False
islower(): True
isupper(): False
istitle(): False
isspace(): False
isprintable(): True
isascii(): True
文字列: HELLO
isalpha(): True
isalnum(): True
isdigit(): False
isdecimal(): False
isnumeric(): False
islower(): False
isupper(): True
istitle(): False
isspace(): False
isprintable(): True
isascii(): True
文字列:
isalpha(): False
isalnum(): False
isdigit(): False
isdecimal(): False
isnumeric(): False
islower(): False
isupper(): False
istitle(): False
isspace(): True
isprintable(): True
isascii(): True
文字列: Hello World
isalpha(): False
isalnum(): False
isdigit(): False
isdecimal(): False
isnumeric(): False
islower(): False
isupper(): False
istitle(): True
isspace(): False
isprintable(): True
isascii(): True
"""
文字列を検索する
| メソッド | 戻り値 | 特徴 |
|---|---|---|
find() |
インデックス or -1 | 見つからなくても安全 |
index() |
インデックス | 見つからないと例外発生 |
rfind() |
インデックス or -1 | 末尾から探索 |
rindex() |
インデックス | 末尾から探索、例外発生 |
count() |
出現回数 | サブ文字列のカウント |
startswith() |
True/False | 接頭辞チェック |
endswith() |
True/False | 接尾辞チェック |
s = "Python programming with Python"
print(s.find("Python")) # 0
print(s.rfind("Python")) # 22
print(s.index("pro")) # 7
print(s.count("Python")) # 2
print(s.startswith("Py")) # True
print(s.endswith("on")) # False
文字列の前後から文字を除去する
| メソッド | 除去対象 | デフォルト動作 | 引数を指定した場合 |
|---|---|---|---|
strip() |
両端(先頭+末尾) | 空白文字(スペース・改行・タブなど) | 指定した文字集合を削除 |
lstrip() |
先頭のみ | 空白文字 | 指定した文字集合を削除 |
rstrip() |
末尾のみ | 空白文字 | 指定した文字集合を削除 |
s = " Python "
print(s.strip()) # "Python"
print(s.lstrip()) # "Python "
print(s.rstrip()) # " Python"
s2 = "---Data---"
print(s2.strip("-")) # "Data"
print(s2.lstrip("-")) # "Data---"
print(s2.rstrip("-")) # "---Data"
文字列に特定の文字列が含まれるか判定する
| 方法 | 判定内容 | 特徴 | 例 |
|---|---|---|---|
in |
部分文字列が含まれているか | 汎用的、位置までは限定できない |
"thon" in "Python" → True |
startswith() |
指定の接頭辞で始まるか | 接頭辞限定、複数指定可(タプル) |
"Python".startswith("Py") → True |
endswith() |
指定の接尾辞で終わるか | 接尾辞限定、拡張子チェックに便利 |
"data.csv".endswith(".csv") → True |
s = "Python programming"
print("thon" in s) # True
print("Java" in s) # False
print("gram" not in s) # False
print(s.startswith("Py")) # True
print(s.startswith("pro")) # False
print(s.startswith(("Java", "Py"))) # True
print(s.startswith("pro", 7)) # True
print(s.endswith("ing")) # True
print(s.endswith("program")) # False
print(s.endswith(("on", "ing"))) # True
print(s.endswith("Python", 0, 6)) # True
文字列を特定の区切り文字で分割する
| メソッド | 分割方向 | 特徴 |
|---|---|---|
split() |
左から順に | デフォルトで空白文字全般を区切りにする |
rsplit() |
右から順に | 末尾側から制御したい場合に便利 |
s = "Python is fun and powerful"
# split: 左から分割
print(s.split())
# ['Python', 'is', 'fun', 'and', 'powerful']
# maxsplit を指定
print(s.split(maxsplit=2))
# ['Python', 'is', 'fun and powerful']
# rsplit: 右から分割
print(s.rsplit(maxsplit=2))
# ['Python is fun and', 'powerful']
# sep を指定
print(s.split(" "))
# ['Python', 'is', 'fun', 'and', 'powerful']
リストの結合
| 構文 | 説明 | 例 | 出力 |
|---|---|---|---|
" ".join(list) |
スペース区切り | " ".join(["A","B","C"]) |
"A B C" |
",".join(list) |
カンマ区切り | ",".join(["A","B","C"]) |
"A,B,C" |
"".join(list) |
区切りなしで結合 | "".join(["A","B","C"]) |
"ABC" |
"/".join(list) |
パス風結合 | "/".join(["usr","bin"]) |
"usr/bin" |
words = ["Python", "is", "fun"]
# 半角スペースで連結
print(" ".join(words))
# "Python is fun"
# カンマで連結
print(",".join(words))
# "Python,is,fun"
# ハイフンで連結
print("-".join(words))
# "Python-is-fun"
この場合、要素はすべて文字列型である必要がある。数値が混じっている場合は、map 関数やリスト内包表記を用いるようにする。
# map関数
nums = [1, 2, 3]
print(",".join(map(str, nums)))
# "1,2,3"
# リスト内包表記
nums = [1, 2, 3]
print(",".join([str(n) for n in nums]))
# "1,2,3"
文字列を置き換える
| 観点 | replace |
translate + maketrans
|
|---|---|---|
| 対象 | 部分文字列 | 1文字(コードポイント) |
| マッピング | 1件ずつ(連鎖で複数) | 複数を一括(表で定義) |
| 速度 | 少数なら十分速い | 多数の単純置換で有利 |
| 削除 | s.replace('x', '') |
Noneで削除(一括指定が簡潔) |
| 複雑さ | 低い | テーブル生成が必要 |
例:'→' を '->'
|
replace('→','->') |
translate({ord('→'):'->'})(OK) |
例:/_\ を全部削除 |
多段 replace が必要 |
maketrans(..., delete="/_\\") で可能 |
# replace
"banana".replace("na", "NA") # 'baNANA'
"aaaa".replace("aa", "b") # 'bb' ← 'aaa'の重なりは置換しない
"2025-09-24".replace("-", "/") # '2025/09/24'
"abc".replace("a", "A", 1) # 'Abc'
# translate
tbl = {ord('a'): 'A', ord('→'): '->', ord('_'): None}
"a→b_c".translate(tbl) # 'A->bc' ('_' は削除)
# maketrans による translate テーブルの作成
tbl = str.maketrans({'/': '-', '_': None, '→': '->'})
"2025/09_24→Done".translate(tbl) # '2025-09 24->Done'
tbl = str.maketrans("abc", "ABC") # 'a'→'A', 'b'→'B', 'c'→'C'
"cab".translate(tbl) # 'CAB'
tbl = str.maketrans({"–":"-"}, "/\\") # スラッシュ類は削除
"A/B\\C–D".translate(tbl) # 'ABCD'('–'→'-'、'/'と'\\'削除)
文字を整形する
format や f-string は特に実例を見た方が早い。
# f-string
name = "Alice"; score = 91.234
s = f"Hello {name}, score={score:.1f}" # 'Hello Alice, score=91.2'
dbg = f"{score=:.2f}" # 'score=91.23'(3.8+ のデバッグ表記)
# format
"{0}, {1:.1f}".format(name, score) # 位置引数
"{n}, {s:.1f}".format(n=name, s=score) # キーワード引数
tmpl = "Hello {n}, score={s:.1f}" # テンプレ再利用に向く
tmpl.format(n=name, s=score)
from datetime import datetime, timezone
def section(title):
print("\n" + "="*10 + f" {title} " + "="*10)
def main():
# 1) 基本
section("1) 基本")
name = "Alice"
score = 91.234
print(f"[f] Hello {name}, score={score:.1f}")
print("[fmt] Hello {0}, score={1:.1f}".format(name, score))
print("[fmt] Hello {n}, score={s:.1f}".format(n=name, s=score))
# 2) 整列・幅・ゼロ埋め・千区切り
section("2) 整列・幅・ゼロ埋め・千区切り")
n = 12345.6789
print(f"[f] |{'title':^10}|")
print(f"[f] |{42:08d}|")
print(f"[f] |{n:,.2f}|")
print("|{:^10}|".format("title"))
print("|{:08d}|".format(42))
print("|{:,.2f}|".format(n))
# 3) 進数・符号・接頭辞
section("3) 進数・符号・接頭辞")
x = 255
print(f"[f] bin={x:#b}, oct={x:#o}, hex={x:#x}, signed={+42:+d}")
print("[fmt] bin={:#b}, oct={:#o}, hex={:#x}, signed={:+d}".format(x, x, x, +42))
# 4) デバッグ表示(!r と {var=})
section("4) デバッグ表示(!r / {var=})")
val = "A\tB"
print(f"[f] {val!r}")
pi = 3.14
print(f"[f] {pi=:.1f}") # 3.8+ のデバッグ表記
print("[fmt] {!r}".format(val))
# 5) 辞書・オブジェクト・インデックスアクセス
section("5) 辞書・オブジェクト・インデックスアクセス")
user = {"name": "Bob", "age": 20}
print(f"[f] {user['name']}({user['age']})")
print("[fmt] {0[name]}({0[age]})".format(user))
class P:
def __init__(self, name): self.name = name
p = P("Carol")
print(f"[f] {p.name}")
print("{p.name}".format(p=p))
# 6) format_map でテンプレ+辞書
section("6) format_map(テンプレ+辞書)")
tmpl = "Hello {who}, price={price:,.0f}"
data = {"who": "Dave", "price": 1234.5}
print("[fmt_map]", tmpl.format_map(data))
# 7) 日付の整形
section("7) 日付の整形")
now = datetime(2025, 9, 24, 20, 0, tzinfo=timezone.utc)
print(f"[f] {now:%Y-%m-%d %H:%M %Z}")
print("[fmt] {:%Y/%m/%d %H:%M}".format(now))
# 8) 表レイアウト(列幅)
section("8) 表レイアウト(列幅)")
rows = [("apple", 12.3), ("banana", 5.0), ("cherry", 1234.56)]
for k, v in rows:
print(f"[f] {k:<10} | {v:>10,.2f}")
for k, v in rows:
print("[fmt] {:<10} | {:>10,.2f}".format(k, v))
# 9) 波括弧のエスケープ
section("9) 波括弧のエスケープ")
print(f"[f] {{config}}")
print("[fmt] {{config}}".format())
# 10) テンプレ再利用 vs コード内で完結
section("10) テンプレ再利用 vs コード内で完結")
tmpl2 = "[{level}] {msg:>20}"
print(tmpl2.format(level="INFO", msg="start"))
print(tmpl2.format(level="WARN", msg="low memory"))
level, msg = "INFO", "done"
print(f"[f] [{level}] {msg:>20}")
# 11) パーセント表記・丸め
section("11) パーセント表記・丸め")
r = 0.0567
print(f"[f] {r:.2%}")
print("{:.2%}".format(r))
# 12) 安全なログ(repr で可視化を統一)
section("12) 安全なログ(repr)")
user_input = "hello\tworld"
print(f"[DBG] user_input={user_input!r}")
if __name__ == "__main__":
main()
リストに関するメソッド
リストから特定範囲の要素を取得する
リストから特定範囲の要素を取り出すには、スライス構文を利用する。
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[2:5]) # [2, 3, 4] (2〜4番目を取得)
print(nums[:4]) # [0, 1, 2, 3] (先頭から3番目まで)
print(nums[6:]) # [6, 7, 8, 9] (6番目以降)
print(nums[::2]) # [0, 2, 4, 6, 8] (偶数番目)
print(nums[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] (逆順)
スライスを用いて、複数要素の追加/置換/削除を行うことが出来る。
# 1. 複数要素の追加(挿入)
nums = [1, 4, 5]
nums[1:1] = [2, 3]
print(nums)
# [1, 2, 3, 4, 5]
# 2. 複数要素の置換
nums = [1, 2, 3, 4, 5]
nums[1:4] = [20, 30]
print(nums)
# [1, 20, 30, 5]
# 3. 複数要素の削除
nums = [1, 2, 3, 4, 5]
nums[1:4] = []
print(nums)
# [1, 5]
# 4. ステップを伴うスライスでの置換
nums = [0, 1, 2, 3, 4, 5, 6]
nums[::2] = [10, 20, 30, 40]
print(nums)
# [10, 1, 20, 3, 30, 5, 40]
# 5. ステップを伴うスライスでの削除(del を使用)
nums = [0, 1, 2, 3, 4, 5, 6]
del nums[::2]
print(nums)
# [1, 3, 5]
リストの要素数を取得する
len 関数でリストに含まれる要素の数を取得できる。
| 関数 | 計算範囲 | 例([[1,2],[3,4,5]]) |
出力 |
|---|---|---|---|
len() |
最上位レベルの要素数 | len([[1,2],[3,4,5]]) |
2 |
recursive_len |
入れ子をすべて展開して総数を数える | recursive_len([[1,2],[3,4,5]]) |
5 |
# 標準の len 関数と、再帰的に要素数を数える recursive_len 関数の比較
def recursive_len(obj):
"""入れ子になったリストやタプルの総要素数を再帰的に数える"""
if isinstance(obj, (list, tuple, set)):
return sum(recursive_len(item) for item in obj)
else:
return 1
# テストデータ
data1 = "Python"
data2 = [1, 2, 3]
data3 = [[1, 2], [3, 4, 5], [6, [7, 8]]]
# len の挙動
print(len(data1)) # 6
print(len(data2)) # 3
print(len(data3)) # 3 (最上位の要素数)
# recursive_len の挙動
print(recursive_len(data1)) # 1 (文字列自体は1要素として扱う)
print(recursive_len(data2)) # 3
print(recursive_len(data3)) # 8 (入れ子を展開して総数を計算)
リストに要素を追加/削除する
リストに要素を追加する際は、append(), insert(), pop() を利用する。
| メソッド | 役割 | 引数 | 戻り値 | 変更後のリスト |
|---|---|---|---|---|
append(x) |
末尾に要素を追加 | 追加する要素 | None |
[1, 2] → [1, 2, x] |
insert(i, x) |
指定位置に要素を挿入 | 位置 i, 要素 x
|
None |
[1, 2] → [1, x, 2] |
pop([i]) |
要素を削除して返す | インデックス(省略可) | 削除された要素 | [1, 2, 3] → [1, 3] |
nums = [1, 2, 3]
# append
nums.append(4)
print(nums) # [1, 2, 3, 4]
# insert
nums.insert(2, 99)
print(nums) # [1, 2, 99, 3, 4]
# pop
removed = nums.pop()
print(removed) # 4
print(nums) # [1, 2, 99, 3]
removed = nums.pop(1)
print(removed) # 2
print(nums) # [1, 99, 3]
cf) スタック構造
append(), pop() を用いて、「後入れ先出し(LOFO)」、「先入れ後出し(FILO)」構造を実装することが出来る。
| 用語 | 意味 | 実現方法 | 取り出し順 |
|---|---|---|---|
| LIFO | 後入れ先出し |
append() + pop()
|
最後に入れたものが最初に出る |
| FILO | 先入れ後出し |
append() + pop()
|
最初に入れたものが最後に出る(LIFOと同義) |
| FIFO | 先入れ先出し |
append() + pop(0)
|
最初に入れたものが最初に出る |
# LIFO / FILO (stack)
stack = []
stack.append("A")
stack.append("B")
stack.append("C")
print(stack.pop()) # C
print(stack.pop()) # B
print(stack.pop()) # A
# FIFO (queue)
queue = []
queue.append("A")
queue.append("B")
queue.append("C")
print(queue.pop(0)) # A
print(queue.pop(0)) # B
print(queue.pop(0)) # C
リスト内の要素を削除する
リストから指定された要素を削除するにはremove()、すべての要素を削除するには clear() を用いる。
| メソッド | 役割 | 戻り値 | 特徴 |
|---|---|---|---|
remove(x) |
最初に出現する x を削除 |
None |
存在しない場合はエラー |
clear() |
すべての要素を削除 | None |
空リストにする |
nums = [1, 2, 3, 2, 4]
# remove
nums.remove(2)
print(nums) # [1, 3, 2, 4]
# clear
nums.clear()
print(nums) # []
リスト内包表記や filter, map などの高階関数を使う方法もある。
→ ただし map 関数は要素変換に使うのが本来の用途で、削除に使うと読みづらくなる。(よって非推奨)
nums = [1, 2, 3, 2, 4]
# リスト内包表記
# 値が2の要素をすべて除去
nums_no_2 = [n for n in nums if n != 2]
print(nums_no_2) # [1, 3, 4]
# 偶数だけ除去
nums_no_even = [n for n in nums if n % 2 != 0]
print(nums_no_even) # [1, 3]
# filter関数
# 値が2でないものを残す
nums_no_2 = list(filter(lambda x: x != 2, nums))
print(nums_no_2) # [1, 3, 4]
# 奇数だけ残す
nums_odd = list(filter(lambda x: x % 2 == 1, nums))
print(nums_odd) # [1, 3]
# map関数
# 2 を None に変換し、None を除外
mapped = map(lambda x: None if x == 2 else x, nums)
nums_no_2 = list(filter(None, mapped))
print(nums_no_2) # [1, 3, 4]
リストを検索する
リストの中で特定の要素が登場するインデックスを取得するには index メソッドを用いる。同一の要素が現れるする回数を数えるには、count メソッドを用いる。
| メソッド | 機能 | 戻り値 | 特徴 |
|---|---|---|---|
index(x[, start[, end]]) |
要素 x が最初に現れる位置を返す | インデックス番号(int) | 範囲指定可能、存在しない場合はエラー |
count(x) |
要素 x の出現回数を返す | 出現回数(int) | 存在しなければ 0 |
nums = [10, 20, 30, 20, 40]
# index
print(nums.index(20)) # 1
print(nums.index(20, 2)) # 3
# print(nums.index(99)) # ValueError: 99 is not in list
# count
print(nums.count(20)) # 2
print(nums.count(99)) # 0
リストを複製する
リストの複製には copy メソッドを用いる。
nums = [1, 2, 3]
nums_copy = nums.copy()
print(nums_copy) # [1, 2, 3]
print(nums is nums_copy) # False(別オブジェクト)
元のリストの浅いコピーを返し、元のリストと新しいリストは別オブジェクトとなる。
→ ただし、ネストされた要素(入れ子のネストなど)は共有される。
shallowcopy と deepcopy
copy メソッドによるコピーは、いわゆるシャロ―コピー(浅いコピー)であるため、配下の要素がイミュータブルであれば、コピー先の変更はコピー元にも影響を及ぼす。
import copy
# 浅いコピー
nums = [1, 2, 3]
nums_copy = nums.copy()
print(nums_copy) # [1, 2, 3]
print(nums is nums_copy) # False
# 浅いコピーの落とし穴
nested = [[1, 2], [3, 4]]
shallow = nested.copy()
shallow[0][0] = 99
print(nested) # [[99, 2], [3, 4]]
print(shallow) # [[99, 2], [3, 4]]
# 深いコピー
deep = copy.deepcopy(nested)
deep[0][0] = 111
print(nested) # [[99, 2], [3, 4]]
print(deep) # [[111, 2], [3, 4]]
リストを並べ替える
sort, reverse, sorted, reversed で、
| メソッド | 破壊的操作 | 戻り値 | 対象 | 用途 |
|---|---|---|---|---|
list.sort() |
あり(リストを直接並べ替える) | None |
リストのみ | リストのソート |
list.reverse() |
あり(リストを逆順にする) | None |
リストのみ | リストの逆順 |
sorted() |
なし(新しいリストを返す) | 新しいリスト | 任意のイテラブル | ソート結果を別に保持したい時 |
reversed() |
なし(イテレータを返す) | 逆順イテレータ | 任意のイテラブル | イテレータ処理で逆順にしたい時 |
nums = [3, 1, 4, 1, 5]
# sort
nums.sort()
print(nums) # [1, 1, 3, 4, 5]
# reverse
nums.reverse()
print(nums) # [5, 4, 3, 1, 1]
# sorted
nums = [3, 1, 4, 1, 5]
print(sorted(nums)) # [1, 1, 3, 4, 5]
print(nums) # [3, 1, 4, 1, 5]
# reversed
nums = [1, 2, 3]
print(list(reversed(nums))) # [3, 2, 1]
リストを for ループで処理する方法
enumerate / zip / zip_longest などの関数を利用して、やや込み入ったリストの処理について紹介する
| 機能 | 構文 | 特徴 | 利用例 |
|---|---|---|---|
enumerate |
enumerate(iterable, start=0) |
インデックスを付与 | インデックス+要素を同時に取得 |
zip |
zip(iter1, iter2, ...) |
複数のイテラブルを並行処理、短い方に合わせる | 名前と点数の対応付け |
zip_longest |
zip_longest(iter1, iter2, ..., fillvalue=None) |
複数イテラブルを並行処理、長い方に合わせる(不足分は埋める) | データ長が異なるリストの結合 |
from itertools import zip_longest
# enumerate
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
print(i, fruit)
# 1 apple
# 2 banana
# 3 cherry
# zip
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(name, score)
# Alice 85
# Bob 92
# Charlie 78
# zip_longest
names = ["Alice", "Bob"]
scores = [85, 92, 78]
for name, score in zip_longest(names, scores, fillvalue="N/A"):
print(name, score)
# Alice 85
# Bob 92
# N/A 78
リスト内の要素が True であるかを判定する
リスト内の要素が True であるかを判定するには、all / any を利用する。
| 関数 / 式 | 意味 | 空リストの場合 | 例 |
|---|---|---|---|
all(iterable) |
すべて真なら True
|
True |
all([1,2,3]) → True |
any(iterable) |
1つでも真なら True
|
False |
any([0,0,3]) → True |
not all(iterable) |
すべて真ではない(= 少なくとも1つは偽) | False |
not all([1,0,3]) → True |
not any(iterable) |
すべて偽 | True |
not any([0,0,0]) → True |
values1 = [1, 2, 3]
values2 = [1, 0, 3]
values3 = [0, 0, 0]
print(all(values1)) # True
print(any(values1)) # True
print(all(values2)) # False
print(any(values2)) # True
print(not all(values2)) # True(0が含まれている)
print(not any(values2)) # False(3がある)
print(all(values3)) # False
print(any(values3)) # False
print(not all(values3)) # True
print(not any(values3)) # True(すべて偽)
キュー構造を実装する
キュー構造 → データ構造の一種で、要素を「順番に」処理する仕組み
基本的な構造は以下
from collections import deque
# FIFO キュー
dq = deque()
dq.append("A")
dq.append("B")
dq.append("C")
print(dq.popleft()) # A
print(dq.popleft()) # B
print(dq.popleft()) # C
# LIFO キュー(スタック)
dq = deque()
dq.append("A")
dq.append("B")
dq.append("C")
print(dq.pop()) # C
print(dq.pop()) # B
print(dq.pop()) # A
# rotate と maxlen
dq = deque([1, 2, 3, 4, 5], maxlen=5)
dq.rotate(2)
print(dq) # deque([4, 5, 1, 2, 3])
リスト内包表記
基本的な構文は以下。
[式 for 変数 in iterable if 条件]
短く簡潔にリストを生成でき、通常の for ループより 可読性と処理速度が良い(Python が最適化している)。ネストも可能だが、深くなると可読性が下がるため注意。
| 構文 | 説明 | 例 | 結果 |
|---|---|---|---|
[式 for x in iterable] |
基本 | [x**2 for x in range(5)] |
[0,1,4,9,16] |
[式 for x in iterable if 条件] |
条件付き | [x for x in range(10) if x%2==0] |
[0,2,4,6,8] |
[式_if if 条件 else 式_else for x in iterable] |
if-else を含む | ["even" if x%2==0 else "odd" for x in range(5)] |
['even','odd','even','odd','even'] |
[(x,y) for x in A for y in B] |
ネスト | [(x,y) for x in range(2) for y in range(3)] |
[(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)] |
# 基本
print([x**2 for x in range(5)])
# [0, 1, 4, 9, 16]
# 条件付き
print([x for x in range(10) if x % 2 == 0])
# [0, 2, 4, 6, 8]
# if-else
print(["even" if x % 2 == 0 else "odd" for x in range(5)])
# ['even', 'odd', 'even', 'odd', 'even']
# ネスト
print([(x, y) for x in range(2) for y in range(3)])
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
# 二次元配列のフラット化
matrix = [[1, 2], [3, 4], [5, 6]]
print([x for row in matrix for x in row])
# [1, 2, 3, 4, 5, 6]
セットに関するメソッド
set → 複数の値を束ねる型だが、順番を持たず重複を許さない
set と frozenset という二つの型がある
→ ミュータブルであるかどうかの違い。frozenset はイミュータブルであり、変更に関する演算子/メソッドを利用できない。
set の生成
| 特徴 | set |
frozenset |
|---|---|---|
| ミュータブル | 〇 | ×(イミュータブル) |
| 要素の追加・削除 | 可能 | 不可 |
| 辞書のキーとして使用 | 不可 | 可能 |
| 集合演算(和・積・差など) | 可能 | 可能 |
# set の例
s = {1, 2, 3}
s.add(4)
print(s) # {1, 2, 3, 4}
# frozenset の例
fs = frozenset([1, 2, 3])
# fs.add(4) # AttributeError: 'frozenset' object has no attribute 'add'
print(fs) # frozenset({1, 2, 3})
# frozenset を辞書のキーに利用
d = {fs: "immutable set"}
print(d) # {frozenset({1, 2, 3}): 'immutable set'}
set の基本操作
| 操作 | メソッド | 特徴 |
|---|---|---|
| 1要素追加 | add(elem) |
既存なら無視 |
| 複数追加 | update(iterable) |
リスト・集合などから追加 |
| 削除(エラーあり) | remove(elem) |
存在しなければ KeyError
|
| 削除(エラーなし) | discard(elem) |
存在しなくても安全 |
| 要素を1つ取り出し | pop() |
削除した要素を返す、順序不定 |
| 全削除 | clear() |
空集合にする |
| 列挙 | for item in set |
順序は保証されない |
s = {1, 2, 3}
# 追加
s.add(4)
s.update([5, 6])
print(s) # {1, 2, 3, 4, 5, 6}
# 削除
s.remove(2)
s.discard(99) # エラーにならない
elem = s.pop() # ランダムに1つ削除
print(elem, s)
# 全削除
s.clear()
print(s) # set()
# 列挙
s = {"apple", "banana", "cherry"}
for item in s:
print(item)
print(sorted(s)) # ['apple', 'banana', 'cherry']
要素の有無/包含関係の判定
| メソッド | 意味 | 戻り値 | 演算子 |
|---|---|---|---|
issubset(other) |
自分 ⊆ other | True/False | <= |
issuperset(other) |
自分 ⊇ other | True/False | >= |
isdisjoint(other) |
自分 ∩ other = ∅ | True/False | なし |
a = {1, 2}
b = {1, 2, 3}
c = {3, 4}
# 部分集合かどうか
print(a.issubset(b)) # True
print(b.issubset(a)) # False
# 上位集合かどうか
print(b.issuperset(a)) # True
print(a.issuperset(b)) # False
# 共通要素の有無
print(a.isdisjoint(c)) # True (共通要素なし)
print(b.isdisjoint(c)) # False(3が共通)
和集合/差集合/積集合などを求める
| 演算 | 演算子 | メソッド | 説明 | |
|---|---|---|---|---|
| 和集合 | `A | B` | A.union(B) |
A または B の要素 |
| 積集合 | A & B |
A.intersection(B) |
A かつ B の要素 | |
| 差集合 | A - B |
A.difference(B) |
A のみに含まれる要素 | |
| 対称差 | A ^ B |
A.symmetric_difference(B) |
どちらか一方にのみ含まれる要素 |
a = {1, 2, 3}
b = {3, 4, 5}
# 和集合
print(a | b) # {1, 2, 3, 4, 5}
# 積集合
print(a & b) # {3}
# 差集合
print(a - b) # {1, 2}
# 対称差
print(a ^ b) # {1, 2, 4, 5}
# 更新演算
a = {1, 2, 3}
a |= b
print(a) # {1, 2, 3, 4, 5}
set内包表記
基本的な構文は以下。
{式 for 変数 in iterable if 条件}
| 用途 | 例 | 出力 |
|---|---|---|
| 基本 | {x**2 for x in [1,2,2,3]} |
{1, 4, 9} |
| 条件付き | {x for x in range(10) if x % 2 == 0} |
{0, 2, 4, 6, 8} |
| ネスト | {(x,y) for x in range(2) for y in range(3)} |
{(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)} |
重複が排除され、順序は保証されない。
# 基本
nums = [1, 2, 2, 3, 4, 4, 5]
print({x**2 for x in nums})
# {1, 4, 9, 16, 25}
# 条件付き
print({x for x in range(10) if x % 2 == 0})
# {0, 2, 4, 6, 8}
# ネスト
print({(x, y) for x in range(2) for y in range(3)})
# {(0, 1), (1, 2), (0, 0), (1, 1), (0, 2), (1, 0)}
# 文字列のユニーク文字
print({ch for ch in "hello world" if ch.isalpha()})
# {'e', 'o', 'r', 'w', 'h', 'd', 'l'}
辞書型
辞書型:ユニークな key, value のペアで管理されるデータ構造
言語によっては、ハッシュや連想配列と呼ぶ場合もある
辞書の作成方法
| 方法 | 構文 | 例 | 出力 |
|---|---|---|---|
| 波括弧 | {key: value, ...} |
{"a":1,"b":2} |
{'a':1,'b':2} |
| dict() | dict(key=value, ...) |
dict(a=1, b=2) |
{'a':1,'b':2} |
| ペアリスト | dict([(k,v),...]) |
dict([("a",1),("b",2)]) |
{'a':1,'b':2} |
| zip() | dict(zip(keys,values)) |
dict(zip(["a","b"],[1,2])) |
{'a':1,'b':2} |
| 内包表記 | {k:v for ...} |
{x:x**2 for x in range(3)} |
{0:0,1:1,2:4} |
| fromkeys | dict.fromkeys(keys, value) |
dict.fromkeys(["a","b"],0) |
{'a':0,'b':0} |
| 空辞書 |
{} / dict()
|
{} |
{} |
# 波括弧
d1 = {"apple": 100, "banana": 200}
print(d1) # {'apple': 100, 'banana': 200}
# dict()
d2 = dict(apple=100, banana=200)
print(d2) # {'apple': 100, 'banana': 200}
# ペアのリスト
d3 = dict([("apple", 100), ("banana", 200)])
print(d3) # {'apple': 100, 'banana': 200}
# zip
keys = ["apple", "banana"]
values = [100, 200]
d4 = dict(zip(keys, values))
print(d4) # {'apple': 100, 'banana': 200}
# 内包表記
d5 = {x: x**2 for x in range(3)}
print(d5) # {0: 0, 1: 1, 2: 4}
# fromkeys
d6 = dict.fromkeys(["apple", "banana"], 0)
print(d6) # {'apple': 0, 'banana': 0}
# 空辞書
d7, d8 = {}, dict()
print(d7, d8) # {} {}
ハッシュ表とキーの注意点
辞書は内部的に、ハッシュ表と呼ばれるリストを持つ
ハッシュとは
ハッシュ値:オブジェクトの内容に基づいて計算される固定長の数値
→ 同じ内容のイミュータブルなオブジェクトは、同じハッシュ値を持つ
ハッシュは辞書や集合における「要素の場所」を拘束に特定するために使われる。
Pythonの辞書はハッシュテーブルで実装されている。
dict にキーを登録するとき:
a) key の hash(key) が計算される
b) この値を元に 内部配列の位置(スロット) が決まる
c) 同じハッシュ値(衝突)が発生した場合は 線形探索などで空きスロットを探す
hashable
上の性質から、辞書のキーはハッシュ値を算出可能でなければならない(hashable)。
ハッシュ可能:イミュータブル型
→ int, float, bool, str, tuple(中の要素もハッシュ可能ならOK), frozenset
ハッシュ不可能:ミュータブル型
→ list, set, dict
d = { (1, 2): "OK" } # tuple はキーにできる
# d = { [1, 2]: "NG" } # list はエラー: TypeError: unhashable type: 'list'
まとめ
# 基本の hash
print(hash("apple"))
print(hash(42))
# 辞書のキーにできる
d = {("x", "y"): 1, frozenset({1, 2}): 2}
print(d)
# {('x', 'y'): 1, frozenset({1, 2}): 2}
# 辞書のキーにできない
# d = {[1, 2]: "NG"} # TypeError: unhashable type: 'list'
# 衝突確認(例: 異なる型だが同じハッシュ値の場合)
print(hash(1) == hash(True)) # True だが 1 == True も True → 同じキー扱い
辞書型の基本操作
| 分類 | 操作 | 構文 / メソッド | 特徴 |
|---|---|---|---|
| 取得 | 値取得 | d[key] |
存在しないと KeyError |
| 値取得(安全) | d.get(key, default) |
デフォルト値指定可 | |
| 設定 | 新規追加・更新 | d[key] = value |
キーが存在すれば更新 |
| 複数更新 | d.update({...}) |
複数キー同時処理 | |
| 削除 | キー削除 | del d[key] |
KeyError の可能性 |
| 値を返して削除 | d.pop(key[, default]) |
デフォルト指定可 | |
| 最後の要素削除 | d.popitem() |
LIFO で削除 | |
| 全削除 | d.clear() |
空辞書にする | |
| 判定 | キー存在確認 |
key in d / key not in d
|
値ではなくキーを判定 |
| 列挙 | キー一覧 | d.keys() |
dict_keys オブジェクト |
| 値一覧 | d.values() |
dict_values オブジェクト |
|
| キー・値ペア | d.items() |
for文で k,v に展開可能 |
d = {"apple": 100, "banana": 200}
# 取得
print(d["apple"]) # 100
print(d.get("grape", "N/A")) # N/A
# 設定
d["cherry"] = 300
d.update({"banana": 250, "orange": 400})
print(d)
# {'apple': 100, 'banana': 250, 'cherry': 300, 'orange': 400}
# 削除
print(d.pop("cherry")) # 300
print(d.pop("grape", 0)) # 0
print(d.popitem()) # ('orange', 400)
del d["banana"]
d.clear()
print(d) # {}
# 判定
d = {"apple": 100, "banana": 200}
print("apple" in d) # True
print("grape" not in d) # True
# 列挙
for k, v in d.items():
print(k, v)
# apple 100
# banana 200
複数の辞書の比較と結合
| 操作 | 方法 | 特徴 | |
|---|---|---|---|
| キー比較(等価) | d1.keys() == d2.keys() |
キー集合が等しいか判定 | |
| 部分集合判定 | d1.keys() <= d2.keys() |
d1 のキーが d2 に含まれるか |
|
| 共通キー | d1.keys() & d2.keys() |
共通するキーを返す | |
| 差集合 | d1.keys() - d2.keys() |
片方にしかないキー | |
| 結合(破壊的) | d1.update(d2) |
d1 が更新される |
|
| 結合(非破壊, 3.5+) | {**d1, **d2} |
新しい辞書を作成 | |
| 結合(非破壊, 3.9+) | `d1 | d2` | 新しい辞書を作成 |
| 結合(破壊的, 3.9+) | `d1 | = d2` |
d1 を更新 |
d1 = {"a": 1, "b": 2, "c": 3}
d2 = {"a": 9, "b": 8}
# キー比較
print(d1.keys() == d2.keys()) # False
print(d2.keys() <= d1.keys()) # True
print(d1.keys() & d2.keys()) # {'a', 'b'}
print(d1.keys() - d2.keys()) # {'c'}
# 辞書結合
d3 = {"x": 100}
d4 = {"y": 200, "x": 999}
# update (破壊的)
d3.update(d4)
print(d3) # {'x': 999, 'y': 200}
# アンパック (非破壊, 3.5+)
merged1 = {**d1, **d2}
print(merged1) # {'a': 9, 'b': 8, 'c': 3}
# | 演算子 (非破壊, 3.9+)
merged2 = d1 | d2
print(merged2) # {'a': 9, 'b': 8, 'c': 3}
# |= 演算子 (破壊的, 3.9+)
d1 |= d2
print(d1) # {'a': 9, 'b': 8, 'c': 3}
defaultdict:既定値を持つ辞書を定義
defaultdict:標準ライブラリ collections に含まれる辞書のサブクラス。
→ 通常の辞書で存在しないキーにアクセスすると KeyError になるが、
defaultdict では あらかじめ指定したデフォルト値を自動生成して返す。
from collections import defaultdict
d = defaultdict(default_factory)
default_factory:デフォルト値を返す関数(例: int, list, set など)
| 項目 | 内容 |
|---|---|
| モジュール | collections.defaultdict |
| 基本構文 | d = defaultdict(default_factory) |
| デフォルト値 |
int → 0, list → [], set → set() など |
| メリット | KeyError 回避・グルーピングやカウント処理が簡潔 |
| 注意点 |
dict に変換すれば通常の辞書として扱える |
辞書内包表記
{key_expr: value_expr for 変数 in iterable if 条件}
| 用途 | 例 | 出力 |
|---|---|---|
| 基本 | {x: x**2 for x in range(5)} |
{0:0, 1:1, 2:4, 3:9, 4:16} |
| 条件付き | {x: x**2 for x in range(10) if x%2==0} |
{0:0, 2:4, 4:16, 6:36, 8:64} |
| if-else | {x: ("even" if x%2==0 else "odd") for x in range(5)} |
{0:'even',1:'odd',...} |
| ネスト | {(x,y): x*y for x in range(3) for y in range(2)} |
{(0,0):0, (0,1):0, ...} |
| 反転 | {v: k for k,v in d.items()} |
{1:'a', 2:'b', 3:'c'} |
# 基本
print({x: x**2 for x in range(5)})
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 条件付き
print({x: x**2 for x in range(10) if x % 2 == 0})
# {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# if-else
print({x: ("even" if x % 2 == 0 else "odd") for x in range(5)})
# {0: 'even', 1: 'odd', 2: 'even', 3: 'odd', 4: 'even'}
# ネスト
print({(x, y): x*y for x in range(3) for y in range(2)})
# {(0, 0): 0, (0, 1): 0, (1, 0): 1, (1, 1): 1, (2, 0): 2, (2, 1): 2}
# 反転
d = {"a": 1, "b": 2, "c": 3}
print({v: k for k, v in d.items()})
# {1: 'a', 2: 'b', 3: 'c'}