1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python文法講義~文字列やコレクションのメソッド~

Posted at

まえがき

文字列やコレクションに関するメソッドについて一度整理する。講義というより、私が忘れつつある内容を一度整理するというのが主目的。(ただし、この記事で必要なメソッドは網羅できるようにするつもりである)
このあたりの話は、「使い方」が重要なので、いちいち小難しい話を並べるよりも実例を多く示す方がよいと考え、そのスタイルで記述している。

文字列の操作

文字列の操作するための関数/メソッドについて順に扱う。

文字列の長さを取得する

文字列の長さ(文字列)を取得するには、組み込み関数 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'('–'→'-'、'/'と'\\'削除)

文字を整形する

formatf-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 → 複数の値を束ねる型だが、順番を持たず重複を許さない

setfrozenset という二つの型がある
→ ミュータブルであるかどうかの違い。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) keyhash(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'}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?