Skip to content

Python チートシート

Web、データ、AI、自動化のための汎用的で読みやすい言語。

01

はじめに

Hello World とコメント

Python はコメントに # を使用し、ドキュメント文字列には三重引用符を使用します。print() 関数は sep と end パラメータをサポートし、出力フォーマットをカスタマイズできます。ドキュメント文字列は help() と __doc__ でアクセス可能なドキュメントとして機能します。

python
# This is a single-line comment
"""
This is a
multi-line comment (docstring)
"""
print("Hello, World!")        # print to stdout
print("A", "B", "C", sep="-") # A-B-C
print("No newline", end="")   # suppress newline

インデントとコードブロック

ほとんどの言語とは異なり、Python はコードブロックを定義するために中括弧ではなくインデントを使用します。一貫性が重要です — タブとスペースを混用すると SyntaxError が発生します。PEP 8 ではレベルごとに 4 スペースを推奨しています。

python
# Python uses indentation (4 spaces) to define blocks
if True:
    print("inside if")
    if True:
        print("nested block")
print("outside block")

# No braces! Indentation IS the syntax
def func():
    x = 1
    return x + 1

入力と出力

input() は stdin から文字列として読み取ります — 数値が必要な場合は必ず変換してください。変換には int()、float() などを使用します。F-strings(Python 3.6+)が文字列をフォーマットする推奨方法です。

python
# input() always returns a string
name = input("Enter your name: ")
age = int(input("Enter your age: "))  # convert to int

print(f"Hello {name}, you are {age} years old")

# formatted output
print("Pi is approximately {:.2f}".format(3.14159))
print(f"{1000000:,}")  # 1,000,000 with thousands separator

複数文と行継続

セミコロンを使用して 1 行に複数の文を区切ります(慣用的な Python では稀です)。長い行はバックスラッシュで継続するか、()、[]、{} の中で自動的に継続できます。可読性のため、暗黙の継続を優先してください。

python
# multiple statements on one line (discouraged)
a = 1; b = 2; c = 3

# explicit line continuation
total = 1 + 2 + 3 + \
        4 + 5 + 6

# implicit continuation inside brackets
nums = [
    1, 2, 3,
    4, 5, 6
]

result = (1 + 2
          + 3 + 4)

Python の実行

Python スクリプトは 'python script.py' で実行します。REPL は対話的な実験を可能にします。python が Python 2 を指すシステムでは必ず python3 を使用してください。shebang 行により、Unix 上でスクリプトが実行可能になります。

python
# Run a script
# $ python script.py

# Run interactively (REPL)
# $ python
# >>> 2 + 2
# 4

# Shebang line for Unix scripts
#!/usr/bin/env python3

# Check Python version
import sys
print(sys.version)
print(sys.version_info.major)  # 3
02

変数とデータ型

変数と動的型付け

Python は動的型付けを使用します — 変数は実行時に型を変更できます。type() で確認し、isinstance() で検証します。Python 3.6+ は型ヒント(name: str = 'Alice')をサポートし、実行時の強制なしで IDE サポートを提供します。

python
# Python is dynamically typed - no declaration needed
name = "Alice"          # str
age = 30                # int
height = 5.7            # float
is_active = True        # bool
items = [1, 2, 3]       # list

# Type checking
print(type(name))       # <class 'str'>
print(isinstance(age, int))  # True

# Multiple assignment
x, y, z = 1, 2, 3
a = b = 0  # chain assignment

型ヒント(Python 3.6+)

型ヒントはコードの可読性を向上させ、IDE の自動補完と mypy による静的解析を可能にします。実行時には強制されません — Python は動的型付けのままです。None になり得る値には Optional[X] を使用してください。

python
# Variable annotations
name: str = "Alice"
age: int = 30
scores: list[float] = [90.5, 85.0]

# Function annotations
def greet(name: str, times: int = 1) -> str:
    return (f"Hi {name}! " * times).strip()

# Optional and Union
from typing import Optional, Union
def find(id: int) -> Optional[str]:
    return "Alice" if id == 1 else None

# mypy for static type checking
# $ mypy script.py

型変換

Python には組み込みの変換関数があります:int()、float()、str()、bool()、list()、tuple()、set()、dict()。偽値には 0、''、[]、{}、None、False が含まれます。int() はゼロに向かって切り捨て、round() は銀行家丸めを使用します。

python
# String to number
num_str = str(42)          # "42"
num = int("42")            # 42
float_num = float("3.14")  # 3.14

# Number conversions
print(int(3.99))           # 3 (truncates toward zero)
print(int(-3.99))          # -3
print(round(3.14159, 2))   # 3.14

# Boolean conversion
print(bool(0))             # False
print(bool(""))            # False
print(bool([]))            # False
print(bool("anything"))    # True

# Collection conversions
print(list("abc"))         # ['a', 'b', 'c']
print(tuple([1, 2, 3]))    # (1, 2, 3)
print(set([1, 1, 2]))      # {1, 2}

数値型

Python の整数は任意精度を持ちます(オーバーフローなし)。浮動小数点数は IEEE 754 倍精度で、通常の精度問題があります。複素数は組み込みです。ブール値は int のサブクラスです(True==1、False==0)。可読性のため、数値リテラルにアンダースコアを使用してください。

python
# Integers (arbitrary precision)
big = 10 ** 100  # no overflow
print(type(big))  # <class 'int'>

# Floats (IEEE 754 double)
pi = 3.14159
print(0.1 + 0.2)  # 0.30000000000000004

# Complex numbers
z = 3 + 4j
print(z.real, z.imag)  # 3.0 4.0
print(abs(z))          # 5.0

# Boolean is subclass of int
print(isinstance(True, int))  # True
print(True + True)            # 2

# Underscores in numbers (3.6+)
million = 1_000_000
binary = 0b_1010_1010

定数と命名規則

Python には const キーワードがありません — ALL_CAPS 名は慣習のみで定数です(再代入を防ぐものはありません)。PEP 8 は命名を定義しています:変数/関数には snake_case、クラスには PascalCase、定数には ALL_CAPS。先頭のアンダースコアは慣習的に「プライベート」を意味し、二重アンダースコアは名前マングリングを引き起こします。

python
# Python has no true constants - convention only
MAX_SIZE = 100          # ALL_CAPS for constants
PI = 3.14159

# Naming conventions (PEP 8)
variable_name = "snake_case"      # variables, functions
ClassName = "PascalCase"          # classes
CONSTANT_VALUE = 100              # constants
_private_var = "underscore prefix" # private (convention)
__name_mangled = "double underscore" # name mangling

# dunder names (reserved)
__name__, __main__, __init__
03

文字列

文字列メソッド

文字列は不変です — メソッドは新しい文字列を返します。find() は見つからない場合は -1 を返し、index() は ValueError を発生させます。検証には isalpha()/isdigit()/isalnum() を使用してください。str クラスには 40 以上のメソッドがあります — dir(str) で探索してください。

python
s = "Hello, World"

# Case operations
print(s.upper())          # HELLO, WORLD
print(s.lower())          # hello, world
print(s.title())          # Hello, World
print(s.capitalize())     # Hello, world
print(s.swapcase())       # hELLO, wORLD

# Search & replace
print(s.find("World"))    # 7 (index, -1 if not found)
print(s.index("World"))   # 7 (raises ValueError if not found)
print(s.replace("o", "0"))  # Hell0, W0rld
print(s.count("l"))       # 3

# Validation
print("abc".isalpha())    # True
print("123".isdigit())    # True
print("  ".isspace())     # True

文字列フォーマット

F-strings は文字列をフォーマットする現代的で最速かつ最も読みやすい方法です。コロンの後にフォーマット指定をサポートします::.2f は小数点以下 2 桁、:>10 は右揃え幅 10、:, は千位区切り。新しいコードでは %-フォーマットを避けてください。

python
name = "Alice"
age = 30

# f-strings (Python 3.6+) - PREFERRED
print(f"Hello, {name}! You are {age}.")
print(f"{name.upper()} is {age * 365} days old")
print(f"{3.14159:.2f}")      # 3.14
print(f"{42:>10}")           # right-align
print(f"{42:<10}")           # left-align
print(f"{42:^10}")           # center
print(f"{1000000:,}")        # 1,000,000

# str.format() method
print("Hello, {}!".format(name))
print("{name} is {age}".format(name="Bob", age=25))

# Old style (avoid in new code)
print("Hello, %s!" % name)

スライスとインデックス

Python のスライス構文 [start:stop:step] は強力です — stop は排他的です。負のインデックスは末尾から数えます。s[::-1] は文字列を反転させる慣用的な方法です。スライスは安全です:範囲外のインデックスはエラーを発生させず、空の文字列を返します。

python
s = "Hello, World"

# Indexing (0-based, negative from end)
print(s[0])        # H
print(s[-1])       # d
print(s[7])        # W

# Slicing [start:stop:step]
print(s[0:5])      # Hello
print(s[7:])       # World
print(s[:5])       # Hello
print(s[::2])      # HloWrd (every 2nd char)
print(s[::-1])     # dlroW ,olleH (reverse!)

# Length
print(len(s))      # 12

# Slicing never raises IndexError
print(s[100:200])  # '' (empty string)

分割と結合

split() は文字列をリストに分割し、join() はリストを文字列に結合します。多くの文字列を効率的に連結するには常に join() を使用してください — + 演算子は中間文字列を生成します。partition() は正確に 3 つの部分(前、区切り文字、後)に分割します。

python
# Split
csv = "a,b,c,d"
print(csv.split(","))          # ['a', 'b', 'c', 'd']
print(csv.split(",", 2))       # ['a', 'b', 'c,d'] (max 2 splits)

# Splitlines
text = "line1\nline2\nline3"
print(text.splitlines())       # ['line1', 'line2', 'line3']

# Partition (splits on first occurrence)
print("[email protected]".partition("@"))
# ('user', '@', 'domain.com')

# Join
words = ["Hello", "World"]
print(" ".join(words))         # Hello World
print("-".join(["2024", "01", "15"]))  # 2024-01-15
print("".join(["a", "b", "c"])) # abc

# String concatenation
s = "Hello" + " " + "World"
parts = ["a"]
parts += "b"  # NOT string concat - adds chars to list!

削除とパディング

strip() はデフォルトで前後の空白を削除し、指定した文字も削除できます。zfill() は先頭にゼロを埋めます(ID に便利)。rjust/ljust/center は指定した幅にパディングし、オプションで埋め文字を指定できます。

python
# Strip whitespace (or specified chars)
s = "  hello  "
print(s.strip())    # "hello"
print(s.lstrip())   # "hello  "
print(s.rstrip())   # "  hello"

# Strip specific characters
print("xxxhelloxxx".strip("x"))  # hello

# Padding / centering
print("42".zfill(5))      # 00042
print("hi".rjust(10))     # "        hi"
print("hi".ljust(10, "-")) # "hi--------"
print("hi".center(10, "*")) # "****hi****"

# expandtabs
print("a\tb".expandtabs(4))  # "a   b"

Raw 文字列とエスケープ

Raw 文字列(r'...')はバックスラッシュを文字通りに扱います — 正規表現パターンや Windows ファイルパスに不可欠です。三重引用符文字列は改行を保持します。文字列は繰り返しに * 演算子を、連結に + をサポートします。

python
# Escape sequences
print("Line1\nLine2")     # newline
print("Tab\there")        # tab
print("Quote: \"hi\"")   # escaped quotes
print("Backslash: \\")   # literal backslash

# Raw strings (ignore escapes) - great for regex
path = r"C:\Users\name\file.txt"
regex = r"\d{3}-\d{4}"
print(path)  # C:\Users\name\file.txt

# Triple-quoted strings
multi = """
Multiple
lines
"""

# String multiplication
print("ab" * 3)  # ababab
04

数値と数学

算術演算子

Python には 7 つの算術演算子があります。/ は常に float を返し、// は切り捨て除算(負の無限大に向かって丸める)です。** はべき乗です(^ は XOR です)。% 演算子の結果は C/Java とは異なり、除数の符号を取ります。

python
# Basic operators
print(7 + 3)   # 10  addition
print(7 - 3)   # 4   subtraction
print(7 * 3)   # 21  multiplication
print(7 / 3)   # 2.333...  true division (always float)
print(7 // 3)  # 2   floor division
print(7 % 3)   # 1   modulo (remainder)
print(7 ** 3)  # 343 exponentiation

# Floor division with negatives
print(-7 // 3)  # -3 (rounds toward negative infinity)
print(-7 % 3)   # 2 (result has same sign as divisor)

# Augmented assignment
x = 10
x += 5   # x = x + 5
x **= 2  # x = x ** 2

Math モジュール

math モジュールは数学関数と定数を提供します。すべての三角関数はラジアンを使用します — math.radians()/degrees() で変換してください。math.gcd() は最大公約数を求めます。複素数には cmath モジュールを使用してください。

python
import math

# Constants
print(math.pi)       # 3.141592653589793
print(math.e)        # 2.718281828459045
print(math.inf)      # inf
print(math.nan)      # nan

# Functions
print(math.sqrt(16))     # 4.0
print(math.pow(2, 10))   # 1024.0
print(math.log(100, 10)) # 2.0 (log base 10)
print(math.log(math.e))  # 1.0 (natural log)
print(math.factorial(5)) # 120
print(math.gcd(12, 8))   # 4

# Rounding
print(math.floor(3.7))   # 3
print(math.ceil(3.2))    # 4
print(math.trunc(-3.7))  # -3 (toward zero)

# Trigonometry (radians)
print(math.sin(math.pi / 2))  # 1.0
print(math.degrees(math.pi))  # 180.0

Random モジュール

random モジュールはメルセンヌツイター PRNG を使用します — 暗号学的に安全ではありません。セキュリティには secrets モジュールを使用してください。random.sample() は一意の項目を選び、random.choices() は重複を許可します。テストで再現可能な結果を得るためにシードを設定してください。

python
import random

# Random integers
print(random.randint(1, 100))    # 1 to 100 inclusive
print(random.randrange(0, 10, 2)) # even number 0,2,4,6,8

# Random floats
print(random.random())           # 0.0 to 1.0
print(random.uniform(1.0, 10.0)) # random float in range

# Choice & sampling
colors = ["red", "green", "blue"]
print(random.choice(colors))           # one random item
print(random.sample(colors, 2))        # 2 unique items
print(random.choices(colors, k=5))     # 5 items (with replacement)

# Shuffle (in-place)
nums = [1, 2, 3, 4, 5]
random.shuffle(nums)
print(nums)

# Reproducible randomness
random.seed(42)  # same seed = same sequence

Decimal と Fractions

浮動小数点の精度エラーが許容されない金融計算(例:お金)には Decimal を使用してください。正確な有理数演算には Fraction を使用してください。どちらも float より遅いですが、丸め誤差を回避します。Decimal は float ではなく文字列から構築してください。

python
from decimal import Decimal, getcontext
from fractions import Fraction

# Float precision issues
print(0.1 + 0.2)  # 0.30000000000000004

# Decimal for exact decimal arithmetic
a = Decimal("0.1")
b = Decimal("0.2")
print(a + b)  # 0.3 (exact!)

# Set precision
getcontext().prec = 6
print(Decimal(1) / Decimal(7))  # 0.142857

# Fractions for exact rational arithmetic
f1 = Fraction(1, 3)
f2 = Fraction(1, 6)
print(f1 + f2)  # 1/2
print(float(f1))  # 0.3333...

# Fraction from string
print(Fraction("3/4"))  # 3/4

ビット演算子

ビット演算子は整数の個々のビットを操作します。Python の整数は任意精度を持つため、シフトは固定幅の言語とは異なる動作をします。一般的な用途:フラグ、マスク、低レベルプロトコルの解析。x & (x-1) == 0 は x が 2 のべき乗かどうかを確認します。

python
# Bitwise operators work on integers
a = 0b1010  # 10
b = 0b1100  # 12

print(a & b)   # 8  (0b1000) AND
print(a | b)   # 14 (0b1110) OR
print(a ^ b)   # 6  (0b0110) XOR
print(~a)      # -11 (NOT, two's complement)
print(a << 2)  # 40 (left shift, multiply by 4)
print(a >> 1)  # 5  (right shift, divide by 2)

# Binary representation
print(bin(10))   # 0b1010
print(hex(255))  # 0xff
print(oct(8))    # 0o10
print(int("1010", 2))  # 10 (parse binary)

# Common tricks
print(5 & 1)     # 1 (check odd: nonzero = odd)
print(8 & (8-1)) # 0 (check power of 2)
05

データ構造

リスト

リストは Python の最も汎用的なデータ構造です — 順序付き、変更可能、異種混合です。append() は O(1)、insert(0, x) は O(n) です。両端での高速な操作には collections.deque を使用してください。sort() はインプレースで、sorted() は新しいリストを返します。

python
# Lists are ordered, mutable sequences
nums = [1, 2, 3, 4, 5]
mixed = [1, "hello", True, 3.14]

# Adding elements
nums.append(6)           # [1,2,3,4,5,6]
nums.insert(0, 0)        # [0,1,2,3,4,5,6]
nums.extend([7, 8])      # extend with another list

# Removing elements
nums.remove(0)           # remove by value
popped = nums.pop()      # remove & return last
popped = nums.pop(0)     # remove & return by index
del nums[0]              # delete by index
nums.clear()             # remove all

# Slicing (same as strings)
nums = [1, 2, 3, 4, 5]
print(nums[1:3])    # [2, 3]
print(nums[::-1])   # [5, 4, 3, 2, 1] reverse

# Sorting
nums.sort()              # in-place sort
nums.sort(reverse=True)  # descending
sorted_nums = sorted(nums)  # returns new list

タプル

タプルは不変で、リストより高速です。固定のコレクション、複数の戻り値、辞書のキー(リストはキーにできません)に使用してください。名前付きタプルは可読性のためのフィールド名を提供します。単一要素のタプルには末尾のカンマが必要です。

python
# Tuples are ordered, IMMUTABLE sequences
point = (3, 4)
single = (42,)     # note the comma for single-element tuple
empty = ()

# Packing & unpacking
coordinates = 10, 20, 30  # packing
x, y, z = coordinates     # unpacking
x, y = y, x               # swap values!

# Multiple return values
def min_max(nums):
    return min(nums), max(nums)

lo, hi = min_max([3, 1, 4, 1, 5])

# Named tuples (readable)
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y)       # 3 4
print(p[0], p[1])     # 3 4

# Tuples are immutable but can contain mutable objects
t = (1, [2, 3])
t[1].append(4)  # OK: (1, [2, 3, 4])

辞書

辞書はハッシュマップです — 検索/挿入/削除は平均 O(1) です。キーはハッシュ可能(不変)でなければなりません。Python 3.7 以降、dict は挿入順序を維持します。KeyError を回避するには get() を使用してください。dict 内包表記は辞書を簡潔に作成します。

python
# Dicts are key-value mappings (insertion-ordered since 3.7)
user = {"name": "Alice", "age": 30}

# Access
print(user["name"])          # Alice
print(user.get("email"))     # None (no KeyError)
print(user.get("email", "N/A"))  # N/A (default)

# Add/update
user["email"] = "[email protected]"    # add
user["age"] = 31             # update
user.setdefault("role", "user")  # set if missing

# Delete
del user["email"]
val = user.pop("age")        # remove & return
# user.clear()               # remove all

# Iteration
for key in user:             # keys
    print(key)
for k, v in user.items():    # key-value pairs
    print(k, v)
for v in user.values():      # values
    print(v)

# Dict comprehension
squares = {x: x**2 for x in range(5)}

# Merge dicts (3.9+)
merged = {"a": 1} | {"b": 2}

セット

セットは一意でハッシュ可能な要素の順序なしコレクションです。メンバーシップテスト(リストの O(n) に対して O(1))と集合演算(和、積、差)に優れています。frozenset は不変でハッシュ可能です。順序は保証されません。

python
# Sets are unordered collections of unique elements
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# Set operations
print(a | b)    # union: {1, 2, 3, 4, 5, 6}
print(a & b)    # intersection: {3, 4}
print(a - b)    # difference: {1, 2}
print(a ^ b)    # symmetric difference: {1, 2, 5, 6}

# Methods
a.add(5)        # add element
a.discard(10)   # remove if present (no error)
a.remove(1)     # remove (KeyError if missing)
a.update([6, 7]) # add multiple

# Membership test (O(1) - faster than list)
print(3 in a)   # True

# Frozen set (immutable)
fs = frozenset([1, 2, 3])

# Common use: deduplicate
unique = list(set([1, 1, 2, 2, 3]))  # [1, 2, 3]

内包表記

内包表記はコレクションを簡潔に作成する Pythonic な方法です。ジェネレータ式(括弧の代わりに丸括弧)は遅延評価です — 要求に応じて値を生成し、メモリを節約します。可読性のため、map()/filter() より内包表記を優先してください。

python
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
pairs = [(x, y) for x in range(3) for y in range(3)]

# Dict comprehension
square_map = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Set comprehension
unique_lens = {len(w) for w in ["a", "ab", "abc", "ab"]}

# Generator expression (lazy, memory-efficient)
gen = (x**2 for x in range(1000000))
print(next(gen))  # 0
print(next(gen))  # 1
total = sum(x**2 for x in range(100))  # no extra list

# Nested comprehension (matrix)
matrix = [[i * 3 + j for j in range(3)] for i in range(3)]
# [[0,1,2], [3,4,5], [6,7,8]]

Collections モジュール

collections モジュールは特殊なコンテナを提供します。Counter はハッシュ可能な項目をカウントします。defaultdict は欠落キーを自動作成します。deque は両端で O(1) の append/pop を提供します(リストの O(n) に対して)。これらはクリーンで効率的なコードに不可欠です。

python
from collections import Counter, defaultdict, deque, OrderedDict

# Counter - counting
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
cnt = Counter(words)
print(cnt)  # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(cnt.most_common(2))  # [('apple', 3), ('banana', 2)]

# defaultdict - no KeyError
dd = defaultdict(list)
dd["fruits"].append("apple")
dd["fruits"].append("banana")
# dd["vegs"] automatically creates empty list

# deque - fast double-ended queue
dq = deque([1, 2, 3])
dq.appendleft(0)   # [0, 1, 2, 3]
dq.append(4)       # [0, 1, 2, 3, 4]
dq.popleft()       # 0, deque is now [1, 2, 3, 4]
dq.rotate(1)       # rotate right

# OrderedDict (less needed since 3.7, dicts are ordered)
od = OrderedDict([("a", 1), ("b", 2)])
06

制御フロー

If / Elif / Else

Python は if/elif/else を使用します — 'elseif' ではなく 'elif' であることに注意してください。インデントがブロックを定義します。三項演算子 'x if cond else y' は式です。Python は空のコレクション、0、None、False を偽として扱います — 簡潔な条件分岐に便利です。

python
score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"

print(f"Grade: {grade}")  # Grade: B

# Conditional expression (ternary)
status = "pass" if score >= 60 else "fail"

# Truthy/falsy values
# Falsy: False, 0, 0.0, "", [], {}, (), None
# Everything else is truthy
if []:    # False
    print("never")
if [0]:   # True (non-empty list)
    print("always")

For ループと反復

Python の for ループは任意のイテラブルを反復します。range() は数値を生成します(stop は排他的)。enumerate() は項目とインデックスをペアにします。zip() は複数のシーケンスを並列で反復します。dict のキーと値のペアを反復するには .items() を使用してください。

python
# range(start, stop, step)
for i in range(5):        # 0, 1, 2, 3, 4
    print(i)
for i in range(2, 10, 2): # 2, 4, 6, 8
    print(i)
for i in range(10, 0, -1): # countdown
    print(i)

# Iterate over collections
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

# enumerate for index + value
for idx, fruit in enumerate(fruits):
    print(f"{idx}: {fruit}")

# zip to iterate multiple sequences
names = ["Alice", "Bob"]
ages = [30, 25]
for name, age in zip(names, ages):
    print(f"{name}: {age}")

# Iterate dict
user = {"name": "Alice", "age": 30}
for key, value in user.items():
    print(f"{key} = {value}")

While ループと Break/Continue

while ループは条件が真の間繰り返します。break はループを即座に終了し、continue は次の反復にスキップします。for/else 構文はループが break されなかった場合にのみ else ブロックを実行します。pass は空のブロックのプレースホルダーです。

python
# Basic while
count = 0
while count < 5:
    print(count)
    count += 1

# break - exit loop
while True:
    cmd = input("> ")
    if cmd == "quit":
        break
    print(f"You said: {cmd}")

# continue - skip to next iteration
for i in range(10):
    if i % 2 == 0:
        continue  # skip even numbers
    print(i)  # prints 1, 3, 5, 7, 9

# else clause (runs if no break)
for i in range(5):
    if i == 10:
        break
else:
    print("Loop completed without break")

# pass - do nothing (placeholder)
for i in range(5):
    pass  # TODO: implement

Match 文(Python 3.10+)

match 文(Python 3.10+)は強力な構造的パターンマッチングで、C の switch をはるかに超えます。シーケンス、マッピング、クラスインスタンスにマッチし、変数をバインドできます。_ パターンはワイルドカード(デフォルト)です。'if' によるガードで条件を追加できます。

python
# Structural pattern matching (like switch)
def handle_command(cmd):
    match cmd.split():
        case ["quit"]:
            return "Goodbye"
        case ["hello", name]:
            return f"Hello, {name}!"
        case ["move", direction] if direction in "NSEW":
            return f"Moving {direction}"
        case ["add", x, y]:
            return int(x) + int(y)
        case _:
            return "Unknown command"

print(handle_command("hello Alice"))  # Hello, Alice!

# Matching data structures
match point:
    case (0, 0):
        print("origin")
    case (0, y):
        print(f"on y-axis at {y}")
    case (x, 0):
        print(f"on x-axis at {x}")
    case (x, y):
        print(f"at ({x}, {y})")

イテレータとジェネレータ

ジェネレータは yield を使用して値を遅延生成します — すべての値を前もって計算せず、メモリを節約します。イテレータプロトコル(iter() と next())を実装します。一度使い切ると終了です。大きな/無限のシーケンス、パイプライン、ストリーミングデータにジェネレータを使用してください。

python
# Iterator protocol
nums = [1, 2, 3]
it = iter(nums)
print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3
# next(it)  # StopIteration

# Generator function (uses yield)
def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

for num in count_up_to(5):
    print(num)  # 1, 2, 3, 4, 5

# Infinite generator
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
print(next(fib))  # 0
print(next(fib))  # 1
print(next(fib))  # 1
print(next(fib))  # 2

# Generator expression
squares = (x**2 for x in range(10))
07

関数

関数の定義と呼び出し

関数は def で定義します。デフォルト引数には = を使用します。Python は明確さのためにキーワード引数をサポートします。関数は複数の値を(タプルとして)返すことができます。ドキュメント文字列(三重引用符)は関数を文書化し、help() でアクセスできます。

python
# Basic function
def greet(name):
    return f"Hello, {name}!"

print(greet("Alice"))  # Hello, Alice!

# Default arguments
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Bob"))              # Hello, Bob!
print(greet("Bob", "Hi"))        # Hi, Bob!

# Keyword arguments
print(greet(name="Carol", greeting="Hey"))

# Return multiple values (tuple)
def stats(nums):
    return min(nums), max(nums), sum(nums) / len(nums)

lo, hi, avg = stats([1, 2, 3, 4, 5])

# Docstrings
def add(a, b):
    """Add two numbers and return the result.
    
    Args:
        a: First number
        b: Second number
    Returns:
        Sum of a and b
    """
    return a + b

引数:*args と **kwargs

*args は余分な位置引数をタプルに集め、**kwargs は余分なキーワード引数を dict に集めます。args/kwargs という名前は慣習です。関数呼び出し時に * でシーケンスを、** で dict を展開できます。順序:位置引数、*args、キーワード引数、**kwargs。

python
# *args - variable positional arguments (tuple)
def sum_all(*args):
    return sum(args)

print(sum_all(1, 2, 3))       # 6
print(sum_all(1, 2, 3, 4, 5)) # 15

# **kwargs - variable keyword arguments (dict)
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=30, role="admin")

# Combining all
def func(a, b, *args, **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"kwargs={kwargs}")

func(1, 2, 3, 4, x=5, y=6)
# a=1, b=2, args=(3, 4), kwargs={'x': 5, 'y': 6}

# Unpacking arguments
nums = [1, 2, 3]
print(sum_all(*nums))  # unpack list as args

opts = {"name": "Alice", "age": 30}
print_info(**opts)  # unpack dict as kwargs

Lambda と高階関数

Lambda は単一の式に制限されます — 複雑なロジックには def を使用してください。sorted()、map()、filter() のような高階関数の引数として優れています。ただし、リスト内包表記は map/filter より読みやすいことが多いです。reduce() は functools にあります。

python
# Lambda - anonymous function (single expression)
square = lambda x: x ** 2
print(square(5))  # 25

# Common with sorted, map, filter, reduce
students = [("Alice", 85), ("Bob", 92), ("Carol", 78)]

# Sort by score (key function)
sorted_by_score = sorted(students, key=lambda s: s[1])
# [('Carol', 78), ('Alice', 85), ('Bob', 92)]

# map - apply function to each item
nums = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, nums))
# [2, 4, 6, 8, 10]

# filter - keep items where function returns True
evens = list(filter(lambda x: x % 2 == 0, nums))
# [2, 4]

# reduce - accumulate to single value
from functools import reduce
product = reduce(lambda a, b: a * b, nums)
# 120 (1*2*3*4*5)

# Prefer comprehensions over map/filter
doubled = [x * 2 for x in nums]  # more Pythonic
evens = [x for x in nums if x % 2 == 0]

デコレータ

デコレータは元のコードを変更せずに動作を追加するために関数をラップします。@構文は糖衣構文です。引数付きデコレータは追加のネストレベルが必要です。元の関数のメタデータ(名前、ドキュメント文字列)を保持するには、常に functools.wraps を使用してください。

python
# A decorator modifies a function's behavior
def uppercase_result(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

@uppercase_result
def greet(name):
    return f"hello, {name}"

print(greet("alice"))  # HELLO, ALICE

# Decorator with arguments
def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hi():
    print("Hi!")

say_hi()  # prints "Hi!" three times

# Practical: timing decorator
import time
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.time() - start:.4f}s")
        return result
    return wrapper

# Use functools.wraps to preserve metadata
from functools import wraps
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

スコープとクロージャ

Python は LEGB スコープ順序で名前を解決します:Local、Enclosing、Global、Built-in。関数内でグローバル変数を再バインドするには 'global' を使用してください。囲みスコープの変数を変更するには 'nonlocal'(Python 3)を使用してください。クロージャは囲みスコープを記憶します。

python
# LEGB rule: Local, Enclosing, Global, Built-in
x = "global"

def outer():
    x = "enclosing"
    
    def inner():
        x = "local"
        print(x)  # local
    
    inner()
    print(x)  # enclosing

outer()
print(x)  # global

# global keyword - modify global variable
count = 0
def increment():
    global count
    count += 1

# nonlocal keyword - modify enclosing variable
def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c = make_counter()
print(c())  # 1
print(c())  # 2
print(c())  # 3
08

オブジェクト指向とクラス

クラスとオブジェクト

クラスはデータ(属性)と振る舞い(メソッド)を束ねます。__init__ はコンストラクタです。self はインスタンスを参照します(他の言語の 'this' に相当)。クラス変数は共有され、インスタンス変数はオブジェクトごとです。__str__ はユーザー向け、__repr__ は開発者向けです。

python
class Dog:
    # Class variable (shared by all instances)
    species = "Canis familiaris"
    
    # Constructor
    def __init__(self, name, age):
        # Instance variables
        self.name = name
        self.age = age
    
    # Instance method
    def bark(self):
        return f"{self.name} says Woof!"
    
    # String representation
    def __str__(self):
        return f"Dog({self.name}, {self.age})"
    
    # Official representation (for debugging)
    def __repr__(self):
        return f"Dog(name='{self.name}', age={self.age})"

# Create instances
buddy = Dog("Buddy", 3)
lucy = Dog("Lucy", 5)

print(buddy.bark())       # Buddy says Woof!
print(buddy.name)         # Buddy
print(buddy.species)      # Canis familiaris
print(str(buddy))         # Dog(Buddy, 3)

継承とポリモーフィズム

継承によりクラスは振る舞いを再利用・拡張できます。Python は MRO(メソッド解決順序)による多重継承をサポートし、競合を解決します。ポリモーフィズムは異なる型を統一的に扱うことを可能にします。型チェックには type() ではなく isinstance() を使用してください。

python
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        raise NotImplementedError("Subclass must implement")

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

# Polymorphism - same interface, different behavior
def animal_sound(animal):
    print(animal.speak())

animals = [Dog("Buddy"), Cat("Whiskers")]
for a in animals:
    animal_sound(a)
# Buddy says Woof!
# Whiskers says Meow!

# Multiple inheritance
class Swimmer:
    def swim(self):
        return "swimming"

class Flyer:
    def fly(self):
        return "flying"

class Duck(Animal, Swimmer, Flyer):
    pass

duck = Duck("Donald")
print(duck.swim())  # swimming
print(duck.fly())   # flying

# Check inheritance
print(isinstance(duck, Animal))  # True
print(issubclass(Dog, Animal))   # True

プロパティとカプセル化

Python には真の private/protected はありません — 慣習を使用します。単一のアンダースコア _ は「内部」を意味します。二重アンダースコア __ は名前マングリングを引き起こします(真のプライバシーではありません)。@property はメソッドをゲッター/セッター付きの属性に変え、検証と計算プロパティを可能にします。

python
class Temperature:
    def __init__(self, celsius=0):
        self.celsius = celsius  # uses setter below
    
    # Getter
    @property
    def celsius(self):
        return self._celsius
    
    # Setter
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Below absolute zero!")
        self._celsius = value
    
    # Computed property
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        self.celsius = (value - 32) * 5/9

temp = Temperature(25)
print(temp.fahrenheit)  # 77.0
temp.fahrenheit = 100
print(temp.celsius)     # 37.78...

# Name conventions:
# _name   - protected (convention, not enforced)
# __name  - private (name mangling: _ClassName__name)
# __name__ - dunder (reserved by Python)

クラスメソッドとスタティックメソッド

@staticmethod はクラス名前空間内の単なる関数です — 暗黙の第一引数はありません。@classmethod は第一引数としてクラス(cls)を受け取り、代替コンストラクタ(ファクトリメソッド)や継承対応の振る舞いに便利です。コンストラクタには classmethod を、ユーティリティ関数には staticmethod を使用してください。

python
class MathUtils:
    pi = 3.14159
    
    # Static method - no self/cls, lives in class namespace
    @staticmethod
    def add(a, b):
        return a + b
    
    # Class method - receives the class as first argument
    @classmethod
    def circle_area(cls, radius):
        return cls.pi * radius ** 2
    
    # Alternative constructor (common classmethod use)
    @classmethod
    def from_diameter(cls, diameter):
        return cls()  # would configure instance

# Static: called on class or instance, no special first arg
print(MathUtils.add(2, 3))  # 5

# Class: often used for alternative constructors
print(MathUtils.circle_area(5))  # 78.54...

# Factory pattern with classmethod
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    @classmethod
    def origin(cls):
        return cls(0, 0)
    
    @classmethod
    def from_tuple(cls, coords):
        return cls(*coords)

p1 = Point.origin()
p2 = Point.from_tuple((3, 4))

マジックメソッド(Dunder)

マジックメソッド(dunder メソッド)は演算子オーバーロードとプロトコルの振る舞いを実装します。+ には __add__、== には __eq__、len() には __len__、反復/アンパックには __iter__。これらにより、オブジェクトが Python の組み込み構文や関数で自然に動作します。

python
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    # String representation
    def __str__(self):
        return f"Vector({self.x}, {self.y})"
    
    # Operator overloading
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
    
    # Equality
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
    
    # Length
    def __len__(self):
        return int((self.x**2 + self.y**2) ** 0.5)
    
    # Make it iterable
    def __iter__(self):
        yield self.x
        yield self.y
    
    # Index access
    def __getitem__(self, index):
        return (self.x, self.y)[index]

v1 = Vector(2, 3)
v2 = Vector(4, 5)
print(v1 + v2)        # Vector(6, 8)
print(v1 * 3)         # Vector(6, 9)
print(v1 == Vector(2, 3))  # True
print(len(v1))        # 3
x, y = v1             # unpacking via __iter__
09

エラー処理

Try / Except / Finally

try/except/else/finally:try はリスクのあるコードを実行し、except はエラーをキャッチし、else は例外がなければ実行され、finally は常に実行されます(クリーンアップ)。単なる 'except:' ではなく、具体的な例外をキャッチしてください。クリーンアップが成功時のみ行われるべき場合に else ブロックが便利です。Exception はほとんどのキャッチ可能なエラーの基底です。

python
# Basic exception handling
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}")  # division by zero
finally:
    print("This always runs")

# Multiple exception types
try:
    value = int("abc")
except (ValueError, TypeError) as e:
    print(f"Conversion error: {e}")

# Different handlers for different exceptions
try:
    f = open("nonexistent.txt")
    data = f.read()
except FileNotFoundError:
    print("File not found")
except PermissionError:
    print("No permission")
except Exception as e:
    print(f"Unexpected: {e}")
else:
    print("No exception occurred")
    f.close()
finally:
    print("Cleanup (always runs)")

# Exception hierarchy
# BaseException
#  ├── SystemExit
#  ├── KeyboardInterrupt
#  └── Exception
#       ├── ValueError
#       ├── TypeError
#       ├── KeyError
#       └── ...

例外の送出

raise を使用して例外を送出します。単独の 'raise' は現在の例外を再送出します(except ブロック内)。'raise X from Y' は例外をチェーンし、元の原因を保持します。常に具体的な例外型を送出してください。通常の制御フローに例外を使用するのは避けてください。

python
# Raise an exception
def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero!")
    return a / b

# Re-raise the current exception
def process(data):
    try:
        return parse(data)
    except ValueError:
        print("Logging parse error...")
        raise  # re-raises the same exception

# Raise with context (from)
try:
    int("abc")
except ValueError as e:
    raise RuntimeError("Failed to process input") from e

# Common built-in exceptions
raise ValueError("invalid value")
raise TypeError("wrong type")
raise KeyError("missing key")
raise IndexError("out of range")
raise RuntimeError("something went wrong")
raise NotImplementedError("override this")
raise FileNotFoundError("no such file")

# Exception with custom args
class ValidationError(Exception):
    pass

raise ValidationError("field is required", "email")

カスタム例外

Exception(またはより具体的な組み込み例外)をサブクラス化してカスタム例外を作成します。呼び出し元が適切なレベルでキャッチできるよう、階層を設計してください。コンテキストを伝えるためにカスタム属性を追加します。BaseException(SystemExit/KeyboardInterrupt を含む)ではなく Exception から継承してください。

python
# Custom exception hierarchy
class AppError(Exception):
    """Base exception for the application."""
    pass

class DatabaseError(AppError):
    def __init__(self, message, query=None):
        super().__init__(message)
        self.query = query

class ValidationError(AppError):
    def __init__(self, field, message):
        super().__init__(f"{field}: {message}")
        self.field = field
        self.message = message

class AuthenticationError(AppError):
    pass

# Usage
def login(username, password):
    if not username:
        raise ValidationError("username", "is required")
    if password != "secret":
        raise AuthenticationError("Invalid credentials")

# Catching by hierarchy
try:
    login("", "x")
except ValidationError as e:
    print(f"Validation failed: {e.field}")
except AppError as e:
    print(f"App error: {e}")

# Access exception info
import traceback
try:
    1 / 0
except:
    traceback.print_exc()
    print(repr(sys.exc_info()[1]))

コンテキストマネージャ(with 文)

コンテキストマネージャ('with' 文)は __enter__ と __exit__ によりクリーンアップを保証します。ファイル、ロック、データベース接続などのリソースに不可欠です。contextlib.contextmanager はジェネレータを使用して作成を簡素化します。__exit__ は True を返すことで例外を抑制できます。

python
# Context managers handle setup and cleanup
with open("file.txt") as f:
    content = f.read()
# file is automatically closed, even if an error occurs

# Multiple context managers
with open("input.txt") as fin, open("output.txt", "w") as fout:
    fout.write(fin.read())

# Creating a context manager (class-based)
class Timer:
    def __enter__(self):
        import time
        self.start = time.time()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        self.elapsed = time.time() - self.start
        print(f"Elapsed: {self.elapsed:.4f}s")
        return False  # don't suppress exceptions

with Timer() as t:
    # code to time
    sum(range(1000000))

# contextlib for simpler context managers
from contextlib import contextmanager

@contextmanager
def open_db(url):
    db = connect(url)
    try:
        yield db
    finally:
        db.close()

with open_db("localhost") as db:
    db.query("SELECT 1")

アサーションとロギング

assert 文は不変条件のデバッグ用です — Python を -O(最適化)で実行すると削除されます。入力検証にアサーションを使用しないでください。本番コードでは print() の代わりに logging モジュールを使用してください — レベル、フォーマット、出力先をサポートします。

python
# Assertions - for debugging (removed with -O flag)
def divide(a, b):
    assert b != 0, "Divisor cannot be zero"
    return a / b

# Never use assertions for data validation (they can be disabled)
# Use them for invariant checks during development

# Logging (better than print for production)
import logging

# Configure logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s [%(levelname)s] %(message)s"
)

logger = logging.getLogger(__name__)

logger.debug("Detailed info for debugging")
logger.info("Confirmation things are working")
logger.warning("Something unexpected happened")
logger.error("A serious problem")
logger.critical("A fatal error")

# Log exceptions with traceback
try:
    1 / 0
except:
    logger.exception("Failed to divide")  # includes traceback
10

ファイル I/O

ファイルの読み込み

ファイルを開く際は常に 'with' を使用してください — エラーが発生しても自動的に閉じます。プラットフォーム依存のエンコーディング問題を回避するため encoding='utf-8' を指定してください。大きなファイルでは、メモリを節約するために read() ではなく行ごとに反復してください。readlines() はファイル全体をメモリに読み込みます。

python
# Read entire file
with open("file.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print(content)

# Read line by line (memory-efficient for large files)
with open("file.txt", "r") as f:
    for line in f:
        print(line.strip())  # strip removes trailing newline

# Read all lines into a list
with open("file.txt") as f:
    lines = f.readlines()  # ['line1\n', 'line2\n', ...]

# Read specific number of characters
with open("file.txt") as f:
    chunk = f.read(100)  # first 100 chars

# File modes:
# "r"  read (default)
# "w"  write (truncate)
# "a"  append
# "x"  exclusive create (fails if exists)
# "b"  binary mode (e.g., "rb", "wb")
# "+"  read and write (e.g., "r+")

ファイルの書き込み

モード 'w' はファイルを切り詰めます(内容を削除);追記には 'a' を使用してください。writelines() は改行を追加しません — 手動で追加してください。バイナリファイル(画像など)には 'rb'/'wb' を使用してください。seek() はカーソルを移動し、tell() は位置を返します。テキストファイルには常にエンコーディングを指定してください。

python
# Write text (overwrites existing)
with open("output.txt", "w") as f:
    f.write("First line\n")
    f.write("Second line\n")
    
    # writelines doesn't add newlines
    f.writelines(["line3\n", "line4\n"])

# Append to a file
with open("log.txt", "a") as f:
    f.write("New log entry\n")

# Write binary data
with open("data.bin", "wb") as f:
    f.write(b"\x00\x01\x02\x03")

# Read and write simultaneously
with open("file.txt", "r+") as f:
    content = f.read()
    f.seek(0)           # move to beginning
    f.write("Updated")  # overwrite
    f.truncate()        # cut off remaining

# Check if file exists
import os
if os.path.exists("file.txt"):
    print("File exists")

パス処理(pathlib)

pathlib(Python 3.4+)はモダンなオブジェクト指向のパス処理方法です — os.path より優先してください。/ 演算子はプラットフォームに依存せずパスを結合します。Path オブジェクトには open/close を処理する read_text()/write_text() メソッドがあります。rglob() は再帰的に検索します。

python
from pathlib import Path

# Create Path objects (preferred over os.path)
p = Path("src/main.py")
home = Path.home()           # /home/user or C:\Users\user
cwd = Path.cwd()             # current working directory

# Path components
print(p.name)        # main.py
print(p.stem)        # main
print(p.suffix)      # .py
print(p.parent)      # src
print(p.parts)       # ('src', 'main.py')

# Joining paths (use / operator)
config = home / ".config" / "app" / "config.json"

# Existence and type
print(p.exists())    # True/False
print(p.is_file())
print(p.is_dir())

# Listing directories
for f in Path(".").iterdir():
    print(f)

# Glob patterns
for py_file in Path(".").rglob("*.py"):
    print(py_file)

# Create directories
Path("new/dir").mkdir(parents=True, exist_ok=True)

# Read/write (Path methods)
content = Path("file.txt").read_text()
Path("output.txt").write_text("Hello!")

JSON

json.dumps() は文字列にシリアライズし、json.loads() はデシリアライズします。dump()/load() はファイルを扱います。pretty-printing には indent を使用してください。カスタムオブジェクトにはデフォルトシリアライザが必要です。JSON は基本型のみサポートします — datetime やその他の複雑なオブジェクトには default= を使用してください。

python
import json

# Python dict to JSON string
data = {"name": "Alice", "age": 30, "skills": ["Python", "SQL"]}
json_str = json.dumps(data, indent=2)
print(json_str)

# JSON string to Python dict
parsed = json.loads('{"name": "Bob", "active": true}')
print(parsed["name"])  # Bob
print(parsed["active"])  # True (Python bool)

# Write JSON to file
with open("data.json", "w") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

# Read JSON from file
with open("data.json") as f:
    loaded = json.load(f)

# Custom serialization (e.g., datetime)
from datetime import datetime
def json_default(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError

json.dumps({"time": datetime.now()}, default=json_default)

# Type mapping:
# JSON object  <-> Python dict
# JSON array   <-> Python list
# JSON string  <-> Python str
# JSON number  <-> Python int/float
# JSON boolean <-> Python bool
# JSON null    <-> Python None

CSV とその他のフォーマット

csv モジュールは適切な引用符付きで CSV を処理します。Windows で CSV ファイルを開く際は newline='' を使用してください。DictReader/DictWriter は列名で動作します。pickle は任意の Python オブジェクトをシリアライズできますが、Python 固有で安全ではありません — 信頼できないソースからのデータを unpickle しないでください。

python
import csv

# Write CSV
with open("data.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["name", "age", "city"])
    writer.writerows([
        ["Alice", 30, "NYC"],
        ["Bob", 25, "LA"],
    ])

# Read CSV
with open("data.csv") as f:
    reader = csv.reader(f)
    header = next(reader)  # first row
    for row in reader:
        print(row)  # ['Alice', '30', 'NYC']

# DictReader/DictWriter (column access by name)
with open("data.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row["name"], row["age"])

# Pickle (Python-specific, can store any object)
import pickle
with open("data.pkl", "wb") as f:
    pickle.dump({"complex": [1, 2, {"a": 3}]}, f)

with open("data.pkl", "rb") as f:
    obj = pickle.load(f)

# WARNING: pickle is insecure - never unpickle untrusted data!
11

モジュールとパッケージ

モジュールのインポート

インポートはモジュールを取り込みます。'import X' は名前空間をクリーンに保ちます。'from X import Y' は便利ですが、名前の衝突を起こす可能性があります。エイリアス(import X as Y)は慣習のあるライブラリ(np、pd)で一般的です。'from X import *' は避けてください — 名前空間を汚染します。

python
# Import entire module
import math
print(math.sqrt(16))

# Import specific names
from datetime import datetime, timedelta
now = datetime.now()

# Import with alias
import numpy as np
import pandas as pd

# Import all names (discouraged - pollutes namespace)
# from os import *

# Conditional import (try/except)
try:
    import cjson as json
except ImportError:
    import json

# Check what's in a module
import os
print(dir(os))         # list all attributes
print(os.__file__)     # module location
print(os.__name__)     # module name

# Reload a module (during development)
import importlib
importlib.reload(my_module)

モジュールとパッケージの作成

モジュールは .py ファイルです;パッケージは __init__.py を含むディレクトリです。__init__.py ファイルは空でもパッケージをセットアップしてもかまいません。__init__.py の __all__ は 'from package import *' がエクスポートするものを制御します。モダンな Python(3.3+)は __init__.py なしで名前空間パッケージをサポートします。

python
# A module is just a .py file
# mymath.py
def add(a, b):
    return a + b

PI = 3.14159

# A package is a directory with __init__.py
# mypackage/
#   __init__.py
#   module1.py
#   module2.py
#   subpackage/
#     __init__.py
#     module3.py

# __init__.py can be empty or contain package initialization
# mypackage/__init__.py
from .module1 import ClassA
from .module2 import func_b
__version__ = "1.0.0"
__all__ = ["ClassA", "func_b"]

# Using the package
from mypackage import ClassA
from mypackage.subpackage import module3

# __all__ controls 'from package import *'
# Without __all__, * imports only what's in __init__.py

__name__ == '__main__'

if __name__ == '__main__' のイディオムにより、ファイルはスクリプトとモジュールの両方として機能できます。直接実行すると __name__ は '__main__' になり、インポート時はモジュール名になります。このパターンは、単独で実行できる再利用可能なモジュールを作るために不可欠です。

python
# script.py
def main():
    print("Running main")

def helper():
    print("Helper function")

if __name__ == "__main__":
    # This code only runs when the file is executed directly
    # NOT when imported as a module
    main()

# When you run: python script.py
# __name__ is "__main__" -> main() runs

# When you: import script
# __name__ is "script" -> main() does NOT run
# This lets the module be both a script and an importable library

# Common pattern for CLI tools
def main():
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("--name", required=True)
    args = parser.parse_args()
    print(f"Hello, {args.name}!")

if __name__ == "__main__":
    main()

標準ライブラリのハイライト

Python の標準ライブラリは巨大でバッテリー同梱です。システム連携には os/sys、日付には datetime、特殊コンテナには collections、イテレータツールには itertools、関数型プログラミングには functools。docs.python.org/3/library/ でドキュメントを探索してください。

python
# os - operating system interface
import os
os.getcwd()           # current directory
os.listdir(".")       # list files
os.environ.get("HOME")  # environment variables

# sys - system-specific
import sys
sys.argv              # command-line arguments
sys.exit(0)           # exit with status code
sys.path              # module search path

# datetime - date and time
from datetime import datetime, timedelta
now = datetime.now()

# collections - specialized containers
from collections import Counter, defaultdict, deque

# itertools - iterator tools
from itertools import chain, cycle, repeat, product

# functools - higher-order functions
from functools import lru_cache, reduce, partial

# typing - type hints
from typing import List, Dict, Optional, Union, Any

# pathlib - path handling
from pathlib import Path

# subprocess - run external commands
import subprocess
result = subprocess.run(["ls", "-l"], capture_output=True, text=True)

Pip と仮想環境

プロジェクトの依存関係を分離するために、常に仮想環境を使用してください。venv は組み込みです;代替には virtualenv、conda、uv があります。再現性のために requirements.txt でバージョンを固定してください。--user や root としてグローバルにパッケージをインストールしないでください — venv を使用してください。

python
# Create a virtual environment
# $ python -m venv venv

# Activate it
# Windows: venv\Scripts\activate
# Unix:    source venv/bin/activate

# Install packages
# $ pip install requests
# $ pip install requests==2.28.0
# $ pip install "requests>=2.25,<3.0"

# Install from requirements file
# $ pip install -r requirements.txt

# requirements.txt example:
# requests==2.31.0
# numpy>=1.21.0
# pandas~=2.0.0  # compatible release

# List installed packages
# $ pip list
# $ pip freeze > requirements.txt

# Uninstall
# $ pip uninstall requests

# Show package info
# $ pip show requests

# Modern alternative: uv (faster)
# $ uv pip install requests
12

日付と時刻

datetime モジュール

datetime モジュールは date、time、datetime、timedelta クラスを提供します。datetime.now() はローカル時刻を返します;UTC には datetime.now(timezone.utc) を使用してください。weekday() は 0-6(月-日)を返します。本番環境では曖昧さを回避するため、常にタイムゾーン対応の datetime を使用してください。

python
from datetime import datetime, date, time, timedelta

# Current date and time
now = datetime.now()          # local time
utc_now = datetime.utcnow()   # UTC (deprecated in 3.12)
utc = datetime.now(timezone.utc)  # preferred

# Current date
today = date.today()

# Create specific date/time
dt = datetime(2024, 1, 15, 10, 30, 0)
d = date(2024, 1, 15)
t = time(10, 30, 0)

# Access components
print(now.year, now.month, now.day)
print(now.hour, now.minute, now.second)
print(now.weekday())  # 0=Monday, 6=Sunday

# From timestamp
ts = 1705315200
dt = datetime.fromtimestamp(ts)

# To timestamp
print(datetime.now().timestamp())

フォーマットと解析

strftime(string format time)は datetime を文字列に変換し、strptime(string parse time)は文字列を datetime に変換します。ISO 8601 フォーマット(isoformat/fromisoformat)は日付を保存する最適な選択です — 曖昧さがなくソート可能です。一般的なコードを覚えてください:%Y %m %d %H %M %S。

python
from datetime import datetime

# Format datetime to string (strftime)
dt = datetime(2024, 1, 15, 10, 30)
print(dt.strftime("%Y-%m-%d"))         # 2024-01-15
print(dt.strftime("%Y/%m/%d %H:%M"))   # 2024/01/15 10:30
print(dt.strftime("%B %d, %Y"))        # January 15, 2024
print(dt.strftime("%A"))               # Monday

# Parse string to datetime (strptime)
dt = datetime.strptime("2024-01-15", "%Y-%m-%d")
dt = datetime.strptime("15/01/2024 10:30", "%d/%m/%Y %H:%M")

# Common format codes:
# %Y  year (2024)     %m  month (01)
# %d  day (15)        %H  hour (14)
# %M  minute (30)     %S  second (00)
# %B  month name      %b  month abbrev
# %A  weekday name    %a  weekday abbrev
# %I  12-hour         %p  AM/PM
# %j  day of year     %U  week number

# ISO format (recommended for storage)
iso = dt.isoformat()  # "2024-01-15T10:30:00"
dt = datetime.fromisoformat("2024-01-15T10:30:00")

timedelta と算術

timedelta は期間を表します。datetime に timedelta を加算/減算でき、2 つの datetime を減算して timedelta を得られます。timedelta は正規化します:days=1, hours=25 は days=2, hours=1 になります。total_seconds() は期間全体を秒で返します。

python
from datetime import datetime, timedelta

now = datetime.now()

# Add/subtract time
tomorrow = now + timedelta(days=1)
last_week = now - timedelta(weeks=1)
in_2_hours = now + timedelta(hours=2)
in_90_days = now + timedelta(days=90)

# Difference between dates
date1 = datetime(2024, 1, 15)
date2 = datetime(2024, 6, 18)
diff = date2 - date1
print(diff.days)         # 155
print(diff.total_seconds())

# timedelta components
td = timedelta(days=5, hours=3, minutes=30)
print(td.days)           # 5
print(td.seconds)        # 12600 (3h 30m in seconds)
print(td.total_seconds())

# Comparisons
if now > date1:
    print("now is later")

# Business day calculation (using numpy)
# import numpy as np
# business_days = np.busday_count(date1.date(), date2.date())

タイムゾーン

常にタイムゾーン対応の datetime を使用してください(Python 3.9+ では pytz より ZoneInfo が推奨されます)。日付は UTC で保存し、表示用にのみローカル時刻に変換してください。ナイーブな datetime(tzinfo なし)は微妙なバグを引き起こします。ZoneInfo は IANA タイムゾーンデータベースを使用し、DST を自動的に処理します。

python
from datetime import datetime, timezone, timedelta

# Timezone-aware datetime
utc_time = datetime.now(timezone.utc)
print(utc_time)  # 2024-01-15 10:30:00+00:00

# Create a timezone (offset-based)
tz_ny = timezone(timedelta(hours=-5), "EST")
ny_time = datetime.now(tz_ny)

# Convert between timezones
utc_time = datetime.now(timezone.utc)
ny_time = utc_time.astimezone(timezone(timedelta(hours=-5)))
tokyo_time = utc_time.astimezone(timezone(timedelta(hours=9)))

# Use zoneinfo (Python 3.9+) for IANA timezones
from zoneinfo import ZoneInfo
tz = ZoneInfo("America/New_York")
dt = datetime.now(tz)
print(dt.tzname())  # EST or EDT

# Common IANA timezones:
# "UTC"
# "America/New_York", "America/Los_Angeles"
# "Europe/London", "Europe/Paris"
# "Asia/Tokyo", "Asia/Shanghai"
# "Australia/Sydney"

# Best practice: store UTC, convert for display
utc_stored = datetime.now(timezone.utc)
local_display = utc_stored.astimezone(ZoneInfo("Asia/Shanghai"))
13

正規表現

re モジュールの基礎

re.search() は任意の場所で最初のマッチを見つけます;re.match() は先頭のみ;re.fullmatch() は文字列全体がマッチする必要があります。バックスラッシュエスケープの問題を回避するため、パターンには raw 文字列(r'...')を使用してください。Match オブジェクトは group()、start()、end()、span() を提供します。

python
import re

# re.search - find first match anywhere in string
m = re.search(r"\d{4}", "Order #2024 was placed")
if m:
    print(m.group())  # 2024
    print(m.start(), m.end())  # 9 13

# re.match - match at beginning of string
m = re.match(r"Hello", "Hello, World")
print(m.group())  # Hello

# re.fullmatch - entire string must match
m = re.fullmatch(r"\d+", "12345")
print(bool(m))  # True

# re.findall - all matches as list
emails = re.findall(r"\S+@\S+", text)
numbers = re.findall(r"\d+", "a1b22c333")  # ['1', '22', '333']

# re.finditer - all matches as iterator (with positions)
for m in re.finditer(r"\w+", "Hello World"):
    print(m.group(), m.span())

# Match object methods
m = re.search(r"(\w+)@(\w+)", "[email protected]")
print(m.group())   # user@domain (whole match)
print(m.group(1))  # user (first group)
print(m.group(2))  # domain (second group)
print(m.groups())  # ('user', 'domain')

パターン構文

正規表現構文:文字クラスには []、一般的なセットには \d \w \s、繰り返しには量指定子(* + ? {})、アンカーには ^ $ \b、グループには ()、選択には |。バックスラッシュを文字通りにするため raw 文字列(r'...')を使用してください。貪欲な量指定子は可能な限りマッチします;遅延にするには ? を追加(例:*?)。

python
import re

# Character classes
re.findall(r"[aeiou]", "hello")      # vowels
re.findall(r"[^aeiou]", "hello")     # non-vowels
re.findall(r"[a-z]", "Hello123")     # lowercase
re.findall(r"[A-Za-z0-9]", "Hi-1!")  # alphanumeric

# Predefined classes
# .   any char except newline
# \d  digit [0-9]      \D  non-digit
# \w  word char [a-zA-Z0-9_]  \W  non-word
# \s  whitespace       \S  non-whitespace

# Quantifiers
# *     0 or more
# +     1 or more
# ?     0 or 1
# {n}   exactly n
# {n,}  n or more
# {n,m} between n and m
re.findall(r"\d{3}", "1234567")      # ['123', '456']
re.findall(r"\d{2,4}", "12345678")   # ['1234', '5678']

# Anchors
# ^  start of string    $  end of string
# \b word boundary
re.findall(r"^\w+", "Hello World")   # ['Hello']
re.findall(r"\b\w+\b", "hi there") # ['hi', 'there']

# Groups & alternation
re.findall(r"(cat|dog)", "cat and dog")  # ['cat', 'dog']
re.findall(r"(\w+)@(\w+\.\w+)", "[email protected]")

置換と分割

re.sub() はマッチを置換します — グループを参照するには後方参照(\1、\2)を、動的置換には関数を使用してください。re.split() は str.split() より強力です — 正規表現パターンを受け入れます。パターン内のキャプチャグループは結果に含まれます。(result, count) を得るには re.subn() を使用してください。

python
import re

# re.sub - replace matches
result = re.sub(r"\d+", "#", "a1b22c333")
# 'a#b#c#'

# Replace with count limit
result = re.sub(r"\d", "X", "a1b2c3", count=2)
# 'aXbXc3'

# Use backreferences in replacement
result = re.sub(r"(\w+)@(\w+)", r"\2.\1", "user@domain")
# 'domain.user'

# Use function as replacement
def upper(m):
    return m.group().upper()

result = re.sub(r"\b[a-z]", upper, "hello world")
# 'Hello World' (capitalize first letter of each word)

# re.split - split by pattern
parts = re.split(r"[,;\s]+", "a, b; c d")
# ['a', 'b', 'c', 'd']

# Split with capture groups (keeps delimiters)
parts = re.split(r"([,;])", "a,b;c")
# ['a', ',', 'b', ';', 'c']

# Split with maxsplit
parts = re.split(r",", "a,b,c,d", maxsplit=2)
# ['a', 'b', 'c,d']

コンパイルとフラグ

パターンを繰り返し使用する場合は re.compile() でコンパイルしてください — より高速です。フラグは動作を変更します:IGNORECASE、MULTILINE、DOTALL、VERBOSE(パターン内のコメント/空白を許可)。名前付きグループ (?P<name>...) は可読性を向上させます。先読み (?=) と後読み (?<=) は消費せずにマッチします。

python
import re

# Compile pattern for reuse (faster when used many times)
email_re = re.compile(r"^[\w.+-]+@([\w-]+\.)+[\w-]+$")
print(email_re.match("[email protected]"))  # match object
print(email_re.match("invalid"))           # None

# Common flags
re.IGNORECASE  # case-insensitive
re.MULTILINE   # ^ and $ match line boundaries
re.DOTALL      # . matches newline too
re.VERBOSE     # allow whitespace & comments in pattern

# Combine flags with |
pattern = re.compile(r"""
    ^           # start of line
    (\w+)      # capture word
    \s+        # whitespace
    (\d+)      # capture number
""", re.VERBOSE | re.MULTILINE)

# Named groups (more readable)
m = re.match(r"(?P<year>\d{4})-(?P<month>\d{2})", "2024-01")
print(m.group("year"))   # 2024
print(m.group("month"))  # 01
print(m.groupdict())     # {'year': '2024', 'month': '01'}

# Lookahead/lookbehind
re.findall(r"\d+(?= dollars)", "100 dollars, 200 euros")
# ['100']  (positive lookahead)
re.findall(r"(?<=\$)\d+", "$100 and $200")
# ['100', '200']  (positive lookbehind)
14

非同期と並行処理

asyncio の基礎

asyncio は Python の非同期 I/O フレームワークです。'async def' はコルーチンを定義し、'await' は結果が ready になるまで中断します。asyncio.run() はイベントループを開始します。asyncio.gather() はコルーチンを並行実行します。コルーチンにより、スレッドなしで高並行 I/O が可能になります。

python
import asyncio

# Define a coroutine
async def greet(name, delay):
    await asyncio.sleep(delay)  # non-blocking sleep
    return f"Hello, {name}!"

# Run a coroutine
async def main():
    result = await greet("Alice", 1)
    print(result)

# Run the event loop
asyncio.run(main())

# Concurrent execution with gather
async def main():
    # Run coroutines concurrently
    results = await asyncio.gather(
        greet("Alice", 2),
        greet("Bob", 1),
        greet("Carol", 3)
    )
    print(results)  # all complete after 3 seconds (max delay)

asyncio.run(main())

# asyncio.create_task - schedule without awaiting immediately
async def main():
    task = asyncio.create_task(greet("Alice", 1))
    # do other work here
    result = await task  # await when needed
    print(result)

非同期 HTTP とタイムアウト

asyncio.timeout()(Python 3.11+)は時間のかかる操作をキャンセルします。HTTP には requests(同期)の代わりに aiohttp(非同期)を使用してください。asyncio.Queue はプロデューサー・コンシューマパターンを可能にします。非同期は I/O バウンドの作業(ネットワーク、ディスク)に理想的です — CPU バウンドの作業には適しません。

python
import asyncio

# Async timeout
async def fetch_with_timeout(url, timeout=5):
    try:
        async with asyncio.timeout(timeout):
            # simulate async operation
            await asyncio.sleep(2)
            return f"Data from {url}"
    except asyncio.TimeoutError:
        return "Request timed out"

# Using aiohttp (third-party) for HTTP
# import aiohttp
# 
# async def fetch(url):
#     async with aiohttp.ClientSession() as session:
#         async with session.get(url) as response:
#             return await response.text()

# Producer-consumer pattern
async def producer(queue):
    for i in range(5):
        await asyncio.sleep(0.1)
        await queue.put(f"item-{i}")
    await queue.put(None)  # sentinel

async def consumer(queue):
    while True:
        item = await queue.get()
        if item is None:
            break
        print(f"Processed: {item}")
        queue.task_done()

async def main():
    queue = asyncio.Queue()
    await asyncio.gather(producer(queue), consumer(queue))

asyncio.run(main())

スレッド

スレッドは I/O バウンドの並行処理(ネットワーク、ファイル I/O)用です。Python の GIL により、スレッドでの真の並列 CPU 実行は防止されます。共有状態を競合状態から保護するには Lock を使用してください。CPU バウンドの作業には multiprocessing を使用してください。スレッドはメモリを共有します;プロセスは共有しません。

python
import threading
import time

# Basic threading
def worker(name, delay):
    print(f"Worker {name} starting")
    time.sleep(delay)
    print(f"Worker {name} done")

# Create and start threads
t1 = threading.Thread(target=worker, args=("A", 2))
t2 = threading.Thread(target=worker, args=("B", 1))
t1.start()
t2.start()

# Wait for threads to complete
t1.join()
t2.join()
print("All done")

# Thread with Lock (for shared state)
counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with lock:  # acquire/release lock
            counter += 1

threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
print(f"Counter: {counter}")  # 500000 (correct with lock)

マルチプロセス

マルチプロセスは GIL をバイパスして真の CPU 並列処理を行います — 各プロセスには独自の Python インタープリタがあります。並列 map 操作には Pool を使用してください。concurrent.futures はスレッドとプロセスの両方に統一された API を提供します。Windows では常に if __name__ == '__main__' でガードしてください。

python
from multiprocessing import Process, Pool, Queue
import os

# Basic process
def worker(name):
    print(f"Process {name} PID: {os.getpid()}")

if __name__ == "__main__":
    p = Process(target=worker, args=("A",))
    p.start()
    p.join()

# Process pool for parallel work
def square(x):
    return x * x

if __name__ == "__main__":
    with Pool(4) as pool:  # 4 worker processes
        results = pool.map(square, range(10))
        print(results)  # [0, 1, 4, 9, ..., 81]
    
    # Asynchronous map
    with Pool(4) as pool:
        result = pool.map_async(square, range(10))
        print(result.get())  # blocks until done

# concurrent.futures (higher-level API)
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor

with ProcessPoolExecutor() as executor:
    results = list(executor.map(square, range(10)))

with ThreadPoolExecutor() as executor:
    futures = [executor.submit(square, i) for i in range(10)]
    results = [f.result() for f in futures]
15

デコレータ

基本的なデコレータ

デコレータは元のソースを変更せずに、関数をラップして振る舞いを拡張または変更します。@構文はデコレータ呼び出しの結果を関数名に再代入する糖衣構文です。任意のシグネチャで動作するよう、ラッパーで *args、**kwargs を使用してください。

python
# A decorator is a function that takes a function and returns a new function
def uppercase_result(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

@uppercase_result
def greet(name):
    return f"hello, {name}"

print(greet("world"))  # HELLO, WORLD
# @uppercase_result is sugar for: greet = uppercase_result(greet)

functools.wraps(メタデータの保持)

@wraps がないと、ラップされた関数は元の __name__、__doc__、シグネチャを失います — デバッグツールや help() は 'wrapper' と表示します。メタデータを保持するため、デコレータ内で常に @functools.wraps(func) を使用してください。これはほぼ普遍的なベストプラクティスです。

python
from functools import wraps

def log_calls(func):
    @wraps(func)  # copies __name__, __doc__, __module__
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}({args}, {kwargs})")
        return func(*args, **kwargs)
    return wrapper

@log_calls
def add(a, b):
    """Add two numbers."""
    return a + b

print(add.__name__)  # 'add' (not 'wrapper')
print(add.__doc__)   # 'Add two numbers.'
help(add)            # shows original docstring

引数付きデコレータ

デコレータが引数を取る場合、3 レベルのネストが必要です:ファクトリ(引数を取る)、デコレータ(関数を取る)、ラッパー(呼び出し引数を取る)。@repeat(3) はまず repeat(3) を呼び出し、デコレータを返し、それが関数に適用されます。

python
# A decorator factory: returns the actual decorator
def repeat(times):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = None
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def say_hi(name):
    print(f"Hi, {name}!")

say_hi("Alice")
# Hi, Alice! (printed 3 times)

クラスベースのデコレータ

クラスデコレータは __init__ で関数を格納し、__call__ で呼び出しをインターセプトします。デコレータが状態(呼び出しカウンタやキャッシュなど)を維持する必要がある場合に理想的です。クラスインスタンスが関数を置き換えるため、呼び出すと __call__ がトリガーされます。

python
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
        wraps(func)(self)  # preserve metadata

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} called {self.count} times")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()  # count=1
say_hello()  # count=2
say_hello()  # count=3
print(say_hello.count)  # 3

組み込みデコレータ(@property、@staticmethod、@classmethod)

@property はメソッドを計算済み属性に変えます(括弧なしでアクセス)。@classmethod は第一引数としてクラスを受け取ります — 代替コンストラクタに最適です。@staticmethod は暗黙の第一引数を受け取りません — たまたまクラス名前空間にある関数です。これらが組み合わさって Pythonic な OOP の骨格を形成します。

python
class Circle:
    pi = 3.14159

    def __init__(self, radius):
        self._radius = radius

    @property
    def area(self):
        return Circle.pi * self._radius ** 2

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter / 2)

    @staticmethod
    def is_valid_radius(r):
        return r >= 0

c = Circle(5)
print(c.area)              # 78.54 (no parentheses!)
c.radius = 10             # uses the setter
c2 = Circle.from_diameter(20)  # alternative constructor

スタックされたデコレータ

デコレータをスタックする場合、ボトムアップで適用され(関数に最も近いものが最初に実行)、呼び出し時にはトップダウンで実行されます。つまり @bold が @italic が greet をラップします。結果は玉ねぎの層のようにネストされます。順序は重要です — 逆にすると出力のネストが変わります。

python
from functools import wraps

def bold(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"<b>{func(*args, **kwargs)}</b>"
    return wrapper

def italic(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"<i>{func(*args, **kwargs)}</i>"
    return wrapper

@bold
@italic
def greet(name):
    return f"Hello, {name}"

print(greet("World"))
# <b><i>Hello, World</i></b>
# Applied bottom-up: italic first, then bold
16

ジェネレータとイテレータ

ジェネレータ関数(yield)

ジェネレータは yield を使用して値を遅延生成します — 各 yield の後に実行を一時停止し、next() が呼ばれると再開します。一度にメモリに存在する値は 1 つだけなので、大きなシーケンスや無限シーケンスでメモリ効率に優れます。一度使い切ると、ジェネレータは再利用できません。

python
# A generator uses 'yield' to produce values lazily, one at a time
def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

gen = count_up_to(5)
print(next(gen))  # 1
print(next(gen))  # 2
print(list(gen))  # [3, 4, 5] (exhausts the rest)

# Generators are memory-efficient: they don't build the whole list
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
print([next(fib) for _ in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

ジェネレータ式

ジェネレータ式はリスト内包表記の遅延版です — 括弧の代わりに丸括弧を使用します。サイズに関わらず一定のメモリを使用し、sum()、max()、any() や他のイテレータへの入力に理想的です。ランダムアクセスが不要な場合は、リスト内包表記よりジェネレータ式を優先してください。

python
# Like list comprehensions, but lazy (uses parentheses)
squares_list = [x ** 2 for x in range(10)]      # builds full list
squares_gen  = (x ** 2 for x in range(10))      # lazy generator

print(squares_gen)        # <generator object>
print(next(squares_gen))  # 0
print(next(squares_gen))  # 1

# Memory comparison
import sys
print(sys.getsizeof([x for x in range(10000)]))  # ~87616 bytes
print(sys.getsizeof((x for x in range(10000))))  # ~200 bytes (constant!)

# Use in sum(), list(), any() etc.
total = sum(x ** 2 for x in range(100))  # no extra list created
print(total)  # 328350

イテレータプロトコル(__iter__、__next__)

イテレータプロトコルは __iter__(イテレータを返す)と __next__(次の値を返すか StopIteration を発生させる)を必要とします。イテラブルはループ可能です;イテレータは一度に 1 つの値を生成します。再利用可能なイテラブルの場合、イテラブル(新しいイテレータを返す)とイテレータ(状態を保持)を分離してください。

python
class Range2:
    """A custom iterator that yields even numbers."""
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self  # the object is its own iterator

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        value = self.current
        self.current += 2
        return value

r = Range2(0, 10)
for num in r:
    print(num)  # 0, 2, 4, 6, 8

# An iterable returns a fresh iterator each time __iter__ is called.
# An iterator returns itself and raises StopIteration when exhausted.

send()、throw()、close()

高度なジェネレータメソッドは双方向通信を可能にします:send() はジェネレータに値を渡し(yield の結果になる)、throw() は yield ポイントに例外を注入し、close() はジェネレータを終了します。送信前に next() でジェネレータを「プライム」する必要があります。これらがコルーチンや非同期フレームワークを支えています。

python
def echo():
    while True:
        received = yield  # yield without a value, receives via send()
        print(f"Echo: {received}")

gen = echo()
next(gen)          # prime the generator (advance to first yield)
gen.send("hello")  # Echo: hello
gen.send("world")  # Echo: world

# throw() injects an exception at the yield point
def safe_gen():
    try:
        while True:
            yield
    except ValueError:
        print("Caught ValueError inside generator")

g = safe_gen()
next(g)
g.throw(ValueError, "boom")  # Caught ValueError inside generator

# close() stops the generator (raises GeneratorExit)
gen.close()

ジェネレータパイプライン

ジェネレータパイプラインは遅延プロデューサをチェーンし、データが一度に 1 項目ずつステージを流れます — 各項目は次が読まれる前に完全に処理されます。これにより中間リストの構築を回避し、ストリーミングデータ処理の基盤となります。Unix パイプも概念的には同じように動作します。

python
# Chain generators to build data-processing pipelines
def numbers():
    for i in range(1, 11):
        yield i

def squared(seq):
    for n in seq:
        yield n ** 2

def evens(seq):
    for n in seq:
        if n % 2 == 0:
            yield n

# Each stage processes one item at a time — no intermediate lists
pipeline = evens(squared(numbers()))
print(list(pipeline))  # [4, 16, 36, 64, 100]

# Equivalent with generator expressions:
result = (n for n in (x ** 2 for x in range(1, 11)) if n % 2 == 0)
print(list(result))  # [4, 16, 36, 64, 100]

yield from(委譲)

yield from はすべての yield(および send/throw/close)をサブイテレータに委譲し、ネストされた構造を平坦化し、コルーチンを構成します。再帰ジェネレータに特に強力です — 古典的な使用例は任意にネストされたリストの平坦化です。非同期コードでは、'await' が同じ概念に基づいています。

python
# yield from delegates to a sub-iterator (Python 3.3+)
def flatten(nested):
    for item in nested:
        if isinstance(item, (list, tuple)):
            yield from flatten(item)  # recursive delegation
        else:
            yield item

data = [1, [2, 3, [4, 5]], 6, [7, [8, [9]]]]
print(list(flatten(data)))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# yield from also forwards send()/throw() to the sub-generator,
# making it essential for coroutine composition.
17

コンテキストマネージャ

with 文の基礎

with 文は例外が発生してもリソースが解放される(ファイルが閉じられ、ロックが解放され、接続が返される)ことを保証します。開始時に __enter__ を、終了時に __exit__ を呼び出します。リソース管理には手動の try/finally より 'with' を常に優先してください — より安全で読みやすいです。

python
# 'with' guarantees cleanup even if an exception occurs
with open("data.txt", "r") as f:
    content = f.read()
# f.close() is called automatically here, even if read() raised

# Without 'with' you must manually close:
f = open("data.txt", "r")
try:
    content = f.read()
finally:
    f.close()  # easy to forget!

# Common built-in context managers:
with open("out.txt", "w") as f, open("in.txt") as g:
    f.write(g.read())  # both files close automatically

カスタムコンテキストマネージャ(クラス)

クラスベースのコンテキストマネージャは __enter__(セットアップ、コンテキストオブジェクトを返す)と __exit__(exc_type, exc_val, exc_tb)(クリーンアップ)を実装します。__exit__ 引数は例外が発生した場合に例外情報を受け取ります;True を返すと例外を抑制します。このパターンはデータベーストランザクションのような複雑なセットアップ/ティアダウンに理想的です。

python
class Timer:
    def __init__(self, label="Timer"):
        self.label = label

    def __enter__(self):
        import time
        self.start = time.perf_counter()
        return self  # value bound to 'as' variable

    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        elapsed = time.perf_counter() - self.start
        print(f"{self.label}: {elapsed:.4f}s")
        # Return False (or None) to propagate exceptions
        # Return True to suppress the exception
        return False

with Timer("Processing"):
    total = sum(i ** 2 for i in range(1_000_000))
# Processing: 0.1234s

contextlib.contextmanager

contextlib.contextmanager はジェネレータ関数をコンテキストマネージャに変えます — yield 前のコードが __enter__、yield 後(finally 内)のコードが __exit__ です。単純なケースではクラスより簡潔です。'as' 変数に値を提供するには yield してください。クリーンアップを保証するには try/finally を使用してください。

python
from contextlib import contextmanager
import time

@contextmanager
def timer(label="Timer"):
    start = time.perf_counter()
    try:
        yield  # code inside the 'with' block runs here
    finally:
        elapsed = time.perf_counter() - start
        print(f"{label}: {elapsed:.4f}s")

with timer("My task"):
    sum(i ** 2 for i in range(1_000_000))
# My task: 0.1234s

# You can also yield a value to bind with 'as'
@contextmanager
def open_db(path):
    db = connect(path)
    try:
        yield db
    finally:
        db.close()

with open_db("app.db") as db:
    db.query("SELECT 1")

複数のコンテキストマネージャ

Python 3.10+ では、よりクリーンな構文のために括弧付き複数行 'with' 文が許可されます。動的な数のコンテキストマネージャには、contextlib.ExitStack がグループとして管理し、逆順ですべてを巻き戻します。リソース数が実行時まで不明な場合、ExitStack は不可欠です。

python
# Python 3.10+ supports parenthesized context managers
with (
    open("input.txt") as fin,
    open("output.txt", "w") as fout,
):
    fout.write(fin.read())

# Pre-3.10: nest them or use contextlib.ExitStack
from contextlib import ExitStack

files = ["a.txt", "b.txt", "c.txt"]
with ExitStack() as stack:
    handles = [stack.enter_context(open(f)) for f in files]
    for h in handles:
        print(h.read())

contextlib ユーティリティ(suppress、redirect)

contextlib.suppress は予期される例外の try/except/pass を置き換えます — はるかに読みやすいです。redirect_stdout/redirect_stderr はコンソールに出力されるはずの出力をキャプチャし、テストやロギングに便利です。これらのユーティリティはボイラープレートを回避し、意図を明確にします。

python
from contextlib import suppress, redirect_stdout, redirect_stderr
import io, warnings

# suppress: ignore specific exceptions (cleaner than try/except/pass)
with suppress(FileNotFoundError):
    os.remove("temp.txt")  # no error if file doesn't exist

# redirect_stdout: capture print output
buffer = io.StringIO()
with redirect_stdout(buffer):
    print("This goes to the buffer, not console")
captured = buffer.getvalue()

# redirect_stderr: capture error/warning output
err_buf = io.StringIO()
with redirect_stderr(err_buf):
    warnings.warn("a warning")
print(err_buf.getvalue())  # the warning text

# Also: contextlib.chdir (3.11+) to temporarily change directory
# from contextlib import chdir
# with chdir("/tmp"): ...

非同期コンテキストマネージャ

非同期コンテキストマネージャは __aenter__/__aexit__('a' プレフィックスに注意)と 'async with' 文を使用します。データベース接続や HTTP セッション(例:aiohttp.ClientSession)などの非同期リソースの管理に不可欠です。ブロック内で await や例外が発生してもクリーンアップが実行されます。

python
import asyncio

class AsyncDB:
    async def __aenter__(self):
        print("connecting...")
        await asyncio.sleep(0.1)  # simulate async connect
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("closing...")
        await asyncio.sleep(0.1)  # simulate async close
        return False

    async def query(self, sql):
        return f"result of: {sql}"

async def main():
    async with AsyncDB() as db:
        result = await db.query("SELECT 1")
        print(result)

asyncio.run(main())
# connecting...
# result of: SELECT 1
# closing...
18

型ヒント

基本的な変数と関数のアノテーション

型ヒントは期待される型を文書化しますが、実行時には強制されません — Python は動的型付けのままです。実行前に型エラーをキャッチするには mypy や pyright のような静的チェッカーを使用してください。組み込みジェネリクス(list[str]、dict[str, int])は Python 3.9+ が必要です;古いバージョンでは typing.List、typing.Dict が必要です。

python
# Variable annotations (Python 3.6+)
name: str = "Alice"
age: int = 30
scores: list[float] = [95.5, 88.0, 92.3]
config: dict[str, int] = {"timeout": 30}

# Function annotations
def greet(name: str, excited: bool = False) -> str:
    punctuation = "!" if excited else "."
    return f"Hello, {name}{punctuation}"

print(greet("World", excited=True))  # Hello, World!

# Annotations are optional and NOT enforced at runtime
def add(a: int, b: int) -> int:
    return a + b
add("2", "3")  # runs fine, returns "23" (no error!)

typing モジュール(List、Dict、Tuple、Optional)

typing モジュールは古い Python にジェネリックエイリアスを提供します。3.9 以降、組み込み型を直接使用できます(List[str] の代わりに list[str])。Optional[X] は Union[X, None] の略記です — 関数が None を返す可能性があることを示し、呼び出し元に None ケースの処理を強制するために使用してください。

python
from typing import List, Dict, Tuple, Set, FrozenSet

# Pre-3.9 style (still works, needed for older Python)
names: List[str] = ["Alice", "Bob"]
scores: Dict[str, int] = {"Alice": 95}
point: Tuple[int, int] = (10, 20)
mixed: Tuple[str, int, float] = ("a", 1, 2.0)
variadic: Tuple[int, ...] = (1, 2, 3)  # variable-length

# Python 3.9+ built-in generics (preferred)
names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 95}
point: tuple[int, int] = (10, 20)

# Optional means "could be None"
from typing import Optional
def find_user(uid: int) -> Optional[str]:
    if uid == 1:
        return "Alice"
    return None  # could also just 'return'

Union と Literal 型

Union[X, Y](または 3.10+ では X | Y)は値がいずれかの型になり得ることを意味します。Literal は値を特定の定数に制限します — enum のオーバーヘッドなしの文字列 enum や、オーバーロードされた関数のディスパッチに最適です。mypy は Literal を使用して型を絞り込み、チェック時に無効な引数をキャッチします。

python
from typing import Union, Literal, overload

# Union: value can be one of several types
def process(data: Union[str, bytes]) -> str:
    if isinstance(data, bytes):
        return data.decode("utf-8")
    return data

# Python 3.10+ union syntax with | (preferred)
def process2(data: str | bytes) -> str:
    if isinstance(data, bytes):
        return data.decode("utf-8")
    return data

# Literal: restrict to specific constant values
def set_mode(mode: Literal["r", "w", "a"]) -> None:
    print(f"Mode set to {mode}")

set_mode("r")   # OK
# set_mode("x")  # mypy error: not a valid literal

# Literal for boolean-like flags
Direction = Literal["up", "down", "left", "right"]

TypeVar とジェネリクス

TypeVar はジェネリック型変数を作成し、関数やクラスが型関係を保持できるようにします(例:'入力と同じ型を返す')。bound= でサブタイプに制約するか、TypeVar('T', int, float) のように制約を指定します。ジェネリッククラスは Generic[T] を基底として使用し、パラメータ化されたコンテナになります。

python
from typing import TypeVar, Generic, List

T = TypeVar("T")  # a generic type variable

def first(items: List[T]) -> T:
    return items[0]

# Type inference: T is bound to the argument's type
x: int = first([1, 2, 3])        # T = int
y: str = first(["a", "b", "c"])  # T = str

# Bounded TypeVar: T must be a subtype of Number
from typing import TypeVar
from numbers import Number
N = TypeVar("N", bound=Number)

def sum_all(values: list[N]) -> N:
    total = values[0]
    for v in values[1:]:
        total = total + v
    return total

# Generic class
class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []
    def push(self, item: T) -> None:
        self._items.append(item)
    def pop(self) -> T:
        return self._items.pop()

s: Stack[int] = Stack()
s.push(1)
# s.push("x")  # mypy error

Callable、型エイリアス、Protocol

Callable[[int, str], bool] は int と str を取り bool を返す関数を記述します。型エイリアスは複雑な型に説明的な名前を与えます。Protocol は構造的(ダック)タイピングを可能にします — 適切なメソッドを持つ任意のオブジェクトがプロトコルを満たし、継承は不要です。これは Python のインターフェースに対する答えです。

python
from typing import Callable, Protocol, TypeAlias

# Callable signature: Callable[[ArgTypes], ReturnType]
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

apply(lambda x, y: x + y, 3, 4)  # 7

# Type aliases (3.12+ uses 'type' statement; older uses assignment)
type Vector = list[float]          # 3.12+
Vector2: TypeAlias = list[float]   # 3.10+

def magnitude(v: Vector) -> float:
    return sum(x ** 2 for x in v) ** 0.5

# Protocol: structural typing (duck typing with static checks)
class Drawable(Protocol):
    def draw(self) -> None: ...

def render(obj: Drawable) -> None:
    obj.draw()  # any object with a draw() method works

class Circle:
    def draw(self) -> None:
        print("drawing circle")

render(Circle())  # OK — Circle has draw()

mypy による型チェック

mypy は Python の最も人気のある静的型チェッカーです — コードを実行せずに型ヒントを分析します。None 処理のバグ、誤った引数の型、欠落した戻り値をキャッチします。段階的型付けから始めてください:新しいコードにヒントを追加し、CI で mypy を実行します。新しいプロジェクトには --strict を使用して包括的なアノテーションを強制してください。

python
# Save as example.py, then run:  mypy example.py
from typing import Optional

def divide(a: float, b: float) -> Optional[float]:
    if b == 0:
        return None
    return a / b

result = divide(10, 0)
# Without checking, this would crash at runtime:
# print(result + 1)  # TypeError: NoneType + int

# mypy catches it:
# error: Unsupported operand types for + ("None" and "int")
# fix: check for None first
if result is not None:
    print(result + 1)

# Run strict mode for maximum safety:
#   mypy --strict example.py
# Common strict flags: --disallow-untyped-defs, --no-implicit-optional
19

データクラス

基本的な @dataclass

@dataclass はアノテーション付きフィールドに基づいて __init__、__repr__、__eq__ を自動生成します — データ保持クラスのボイラープレートを排除します。値オブジェクト、設定、DTO、レコードに理想的です。Python 3.7 から利用可能です。フィールドには型アノテーションが必要です;アノテーションがフィールドを定義します。

python
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p1 = Point(1.0, 2.0)
p2 = Point(1.0, 2.0)

print(p1)         # Point(x=1.0, y=2.0)  — auto __repr__
print(p1 == p2)   # True  — auto __eq__ (compares fields)
print(p1.x)       # 1.0

# Without @dataclass you'd write all this boilerplate:
# class Point:
#     def __init__(self, x, y): self.x = x; self.y = y
#     def __repr__(self): ...
#     def __eq__(self, other): ...

デフォルト値と default_factory

変更可能なデフォルト(リスト、dict、セット)は field(default_factory=list) を使用する必要があります — [] を直接使用すると、すべてのインスタンスで 1 つのリストを共有することになり、典型的なバグです。default_factory はインスタンスごとに 1 回呼び出され、新しいオブジェクトを作成します。単純な不変のデフォルト(int、str、bool、None)は直接代入できます。

python
from dataclasses import dataclass, field

@dataclass
class Student:
    name: str
    grade: str = "A"          # simple default
    tags: list[str] = field(default_factory=list)  # mutable default!
    scores: dict[str, int] = field(default_factory=dict)

s = Student("Alice")
print(s)  # Student(name='Alice', grade='A', tags=[], scores={})

s.tags.append("honors")
s2 = Student("Bob")
print(s2.tags)  # [] — each instance gets its own list

# NEVER use [] or {} as a direct default — all instances would share
# the same mutable object (classic Python pitfall).

frozen、eq、order

frozen=True はデータクラスを不変にします — フィールドを再代入できず、インスタンスはハッシュ可能になります(dict のキーや set のメンバーとして使用可能)。order=True はソート用の比較メソッドを追加します。座標、色、バージョンなどの不変でソート可能な値型には frozen=True と order=True を組み合わせてください。

python
from dataclasses import dataclass

# frozen=True makes instances immutable (hashable, usable as dict keys)
@dataclass(frozen=True)
class Color:
    r: int
    g: int
    b: int

c = Color(255, 0, 0)
# c.r = 128  # FrozenInstanceError!
print(hash(c))  # works — frozen dataclasses are hashable

# order=True generates __lt__, __le__, __gt__, __ge__ for sorting
@dataclass(order=True)
class Priority:
    level: int

tasks = [Priority(3), Priority(1), Priority(2)]
tasks.sort()
print(tasks)  # [Priority(level=1), Priority(level=2), Priority(level=3)]

# Common combo: frozen + order for immutable comparable values
@dataclass(frozen=True, order=True)
class Version:
    major: int
    minor: int

__post_init__ とフィールドカスタマイズ

__post_init__ は生成された __init__ の後に自動的に実行されます — 派生フィールドの計算、値の検証、セットアップに使用します。field(init=False) はコンストラクタにないフィールドを作成します(計算/キャッシュ値に適しています)。field(repr=False, compare=False) は repr と等価チェックからフィールドを隠します。

python
from dataclasses import dataclass, field

@dataclass
class User:
    email: str
    _email_normalized: str = field(init=False, repr=False)
    id: int = field(default=0)

    def __post_init__(self):
        # runs after __init__; compute derived fields here
        self._email_normalized = self.email.strip().lower()

u = User("  [email protected] ")
print(u.email)             # '  [email protected] '
print(u._email_normalized) # '[email protected]'

# field(init=False) excludes a field from __init__
# field(repr=False) hides it from the repr
# field(compare=False) excludes from __eq__/__hash__
# field(metadata={...}) attaches custom metadata

継承と slots

データクラスは継承をサポートします — 子フィールドは親フィールドの後に追加され、親のデフォルトをオーバーライドできます。注意:親でデフォルトを持つフィールドの後に、子でデフォルトなしのフィールドを置くことはできません。slots=True(3.10+)は任意の属性の追加を防ぎ、インスタンスごとのメモリを大幅に削減します — 数百万の小さなオブジェクトに理想的です。

python
from dataclasses import dataclass

@dataclass
class Animal:
    name: str
    sound: str = "..."

@dataclass
class Dog(Animal):
    breed: str = "unknown"
    sound: str = "Woof"  # override parent default

d = Dog("Rex", breed="Labrador")
print(d)  # Dog(name='Rex', sound='Woof', breed='Labrador')

# Python 3.10+: slots=True saves memory (no __dict__)
@dataclass(slots=True)
class Pixel:
    r: int
    g: int
    b: int

p = Pixel(0, 128, 255)
# p.new_field = 1  # AttributeError — slots prevent arbitrary attrs
# Saves ~40-50% memory vs regular dataclass for many instances
20

Collections と Itertools

namedtuple

namedtuple は名前付きフィールドを持つタプルのサブクラスを作成します — タプルと同じメモリ効率ですが、はるかに読みやすいです。不変なので、変更されたコピーを作成するには _replace() を使用してください。新しいコードでは型アノテーションとデフォルト値をサポートする typing.NamedTuple を優先してください。関数から複数の値を返すのに最適です。

python
from collections import namedtuple

# Lightweight immutable class with named fields
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y)       # 3 4  — access by name
print(p[0], p[1])     # 3 4  — also by index
print(p._asdict())    # {'x': 3, 'y': 4}

# More memory-efficient than a full class
# Use _replace to create a modified copy (immutable!)
p2 = p._replace(x=10)
print(p2)  # Point(x=10, y=4)

# Python 3.6+ typing.NamedTuple for type hints:
from typing import NamedTuple
class Point3D(NamedTuple):
    x: float
    y: float
    z: float = 0.0

Counter

Counter はハッシュ可能なオブジェクトをカウントする dict のサブクラスです — 頻度分析、ヒストグラム、投票に最適です。most_common(n) は上位 n 項目を返します。欠落キーは KeyError を発生させず 0 を返します。Counter はカウントの集合的演算に +、-、&、| をサポートします。

python
from collections import Counter

# Count hashable items
words = "the cat sat on the mat the cat".split()
c = Counter(words)
print(c)  # Counter({'the': 3, 'cat': 2, 'sat': 1, 'on': 1, 'mat': 1})

# Most common elements
print(c.most_common(2))  # [('the', 3), ('cat', 2)]

# Arithmetic on counters
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2)  # Counter({'a': 4, 'b': 3})
print(c1 - c2)  # Counter({'a': 2})  (drops zero/negatives)

# Missing keys return 0 (not KeyError)
print(c["dog"])  # 0

# Update and elements
c.update(["cat", "cat"])
print(sorted(c.elements()))  # ['cat','cat','cat','cat','mat','on','sat','the','the','the']

defaultdict

defaultdict はファクトリ関数からデフォルト値で欠落キーを自動作成します — グループ化には list、カウントには int、重複排除には set。これにより 'if key not in dict' のボイラープレートが排除されます。ファクトリはキーが欠落している場合にのみ呼び出され、毎回のアクセスでは呼び出されません。

python
from collections import defaultdict

# Group items by key without checking if key exists
words = ["apple", "banana", "avocado", "blueberry", "cherry"]
by_first = defaultdict(list)
for w in words:
    by_first[w[0]].append(w)
print(dict(by_first))
# {'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}

# Counting with int (default 0)
counts = defaultdict(int)
for w in words:
    counts[w[0]] += 1
print(dict(counts))  # {'a': 2, 'b': 2, 'c': 1}

# Nested defaultdicts
tree = defaultdict(lambda: defaultdict(list))
tree["2024"]["Jan"].append("event1")

# vs regular dict: avoids the key-check boilerplate
# d = {}
# for w in words:
#     if w[0] not in d:
#         d[w[0]] = []
#     d[w[0]].append(w)

OrderedDict と deque

deque は両端で O(1) の append/pop を提供します — キュー、BFS、スライディングウィンドウにリストの代わりに使用してください(list.pop(0) は O(n))。maxlen を指定すると、deque は古い項目を自動破棄し、限定バッファに最適です。3.7 以降(dict は順序付き)では OrderedDict の必要性は減りましたが、move_to_end と popitem は LRU キャッシュに依然として独自の有用性を持ちます。

python
from collections import OrderedDict, deque

# deque: double-ended queue, O(1) append/pop at both ends
dq = deque([1, 2, 3], maxlen=5)
dq.appendleft(0)   # deque([0, 1, 2, 3])
dq.append(4)       # deque([0, 1, 2, 3, 4])
dq.append(5)       # deque([1, 2, 3, 4, 5]) — oldest dropped (maxlen!)
print(dq.popleft())  # 1
print(dq)            # deque([2, 3, 4, 5])

# deque is ideal for queues, BFS, sliding windows
from collections import deque
queue = deque(["task1", "task2"])
queue.append("task3")
next_task = queue.popleft()  # FIFO — O(1) vs list.pop(0) which is O(n)

# OrderedDict: remembers insertion order (regular dicts do too in 3.7+,
# but OrderedDict has move_to_end and equality is order-sensitive)
od = OrderedDict([("a", 1), ("b", 2)])
od.move_to_end("a")  # move to last
print(list(od))  # ['b', 'a']
od.popitem(last=False)  # pop first item (FIFO)

itertools:chain、product、combinations、permutations

itertools は組合せ論のための高速でメモリ効率の良いツールを提供します。chain はイテラブルを遅延平坦化します。product はデカルト積を与えます(ネストされた for ループを置き換え)。combinations/permutations は完全なリストを構築せずに選択を生成します — 大きな入力や無限入力に不可欠です。すべてイテレータを返すので、list() でラップして表示してください。

python
from itertools import chain, product, combinations, permutations

# chain: flatten multiple iterables
list(chain([1, 2], [3, 4], [5]))  # [1, 2, 3, 4, 5]
list(chain.from_iterable([[1, 2], [3, 4]]))  # [1, 2, 3, 4]

# product: Cartesian product (nested loops)
list(product([1, 2], ["a", "b"]))
# [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
list(product("AB", repeat=2))  # [('A','A'),('A','B'),('B','A'),('B','B')]

# combinations: unordered selections (no repeats)
list(combinations("ABC", 2))  # [('A','B'),('A','C'),('B','C')]
list(combinations("AAA", 2))  # [('A','A'),('A','A'),('A','A')]

# permutations: ordered arrangements
list(permutations("ABC", 2))  # [('A','B'),('A','C'),('B','A'),('B','C'),('C','A'),('C','B')]

# combinations_with_replacement: allow picking same element
from itertools import combinations_with_replacement
list(combinations_with_replacement("AB", 2))  # [('A','A'),('A','B'),('B','B')]

itertools:groupby、accumulate、starmap

groupby はキーを共有する連続要素をグループ化します — 最初にキーでソートしないと、同じキーで複数のグループができます。accumulate は累計/累積積を生成します。islice、takewhile、dropwhile はスライスとフィルタリングの遅延代替で、無限のものを含む任意のイテレータで動作します。

python
from itertools import groupby, accumulate, starmap, islice, takewhile, dropwhile

# groupby: group consecutive items by a key (sort first!)
data = [("A", 1), ("A", 2), ("B", 3), ("B", 4), ("A", 5)]
data.sort(key=lambda x: x[0])  # MUST sort by key first
for key, group in groupby(data, key=lambda x: x[0]):
    print(key, list(group))
# A [('A',1),('A',2),('A',5)]
# B [('B',3),('B',4)]

# accumulate: running aggregate (sum by default)
list(accumulate([1, 2, 3, 4]))  # [1, 3, 6, 10]
import operator
list(accumulate([1, 2, 3, 4], operator.mul))  # [1, 2, 6, 24]

# starmap: unpack args from tuples before calling
list(starmap(pow, [(2, 3), (3, 2), (10, 3)]))  # [8, 9, 1000]

# islice: slice an iterator (doesn't support negative indices)
list(islice(range(100), 5, 10))  # [5, 6, 7, 8, 9]

# takewhile / dropwhile: filter by predicate
list(takewhile(lambda x: x < 5, [1, 4, 6, 3, 8]))  # [1, 4]
list(dropwhile(lambda x: x < 5, [1, 4, 6, 3, 8]))  # [6, 3, 8]

functools:lru_cache、partial、reduce

lru_cache は結果をメモ化します — 再帰的または高コストの純粋関数の劇的な高速化;cache_info() はヒット/ミス統計を表示します。partial は引数を事前入力して特殊化された呼び出し可能オブジェクトを作成します。reduce は関数を累積的に適用します(ただし sum()、any()、all() がしばしば置き換えます)。cached_property は一度計算してインスタンスにキャッシュします。

python
from functools import lru_cache, partial, reduce
import operator

# lru_cache: memoize function results (Least Recently Used)
@lru_cache(maxsize=128)
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(100))  # instant (without cache: impossibly slow)
print(fib.cache_info())  # CacheInfo(hits=98, misses=101, ...)

# partial: fix some arguments, create a new callable
def power(base, exponent):
    return base ** exponent

square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5))  # 25
print(cube(3))    # 27

# reduce: cumulatively apply a function, reducing to one value
product = reduce(operator.mul, [1, 2, 3, 4])  # 24
# Equivalent: ((1*2)*3)*4

# Python 3.8+: cached_property for lazy computed attributes
from functools import cached_property
class Data:
    @cached_property
    def expensive(self):
        print("computing...")
        return [i ** 2 for i in range(1000000)]
21

JSON と CSV 処理

json.dumps と json.loads

json.dumps()(dump string)は Python オブジェクトを JSON 文字列にシリアライズし、json.loads()(load string)は JSON を解析して戻します。可読性には indent を、Unicode 文字を読みやすく保つには ensure_ascii=False を、決定的出力には sort_keys を使用してください。JSON キーは文字列でなければなりません — int キーは文字列になります。

python
import json

# Serialize Python object to JSON string
data = {"name": "Alice", "age": 30, "scores": [95, 88, 92]}
json_str = json.dumps(data)
print(json_str)  # {"name": "Alice", "age": 30, "scores": [95, 88, 92]}

# Pretty-print with indent
print(json.dumps(data, indent=2))
# {
#   "name": "Alice",
#   "age": 30,
#   "scores": [95, 88, 92]
# }

# Parse JSON string to Python object
parsed = json.loads(json_str)
print(parsed["name"])  # Alice
print(type(parsed["scores"]))  # <class 'list'>

# Sort keys, handle non-ASCII
print(json.dumps({"name": "Zoë"}, ensure_ascii=False, sort_keys=True))

JSON ファイルの読み書き

json.dump() はファイルオブジェクトに直接書き込み、json.load() はファイルから読み取ります。移植性のため常に encoding='utf-8' を指定してください。型マッピングを覚えておいてください:JSON オブジェクトは dict に、配列は list に、数値は int または float になります。datetime、set、カスタムオブジェクトはデフォルトで JSON シリアライズ可能ではありません。

python
import json

data = {"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}

# Write to file
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

# Read from file
with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)

print(loaded["users"][0]["name"])  # Alice

# Type conversions to remember:
# JSON object  <-> Python dict
# JSON array   <-> Python list
# JSON string  <-> Python str
# JSON number  <-> Python int/float
# JSON true/false <-> Python True/False
# JSON null    <-> Python None

カスタム JSON エンコーディング(datetime、カスタムオブジェクト)

json モジュールはデフォルトで datetime、set、カスタムクラスをシリアライズできません。default 関数(シリアライズ不可能なオブジェクトに対して呼び出される)または JSONEncoder サブクラスを提供してください。ラウンドトリップには、loads() の object_hook とカスタムエンコーダをペアにして元の型を再構築します。これが ORM がモデルオブジェクトをシリアライズする方法です。

python
import json
from datetime import datetime

# Default behavior: TypeError on non-serializable types
# json.dumps({"now": datetime.now()})  # TypeError!

# Solution 1: default function for unknown types
def default_encoder(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    if isinstance(obj, set):
        return sorted(obj)
    raise TypeError(f"Cannot serialize {type(obj)}")

data = {"now": datetime.now(), "tags": {"a", "b"}}
print(json.dumps(data, default=default_encoder))

# Solution 2: custom JSONEncoder subclass
class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return {"__datetime__": obj.isoformat()}
        return super().default(obj)

print(json.dumps(data, cls=MyEncoder))

# Decoding with object_hook
def decoder(dct):
    if "__datetime__" in dct:
        return datetime.fromisoformat(dct["__datetime__"])
    return dct
json.loads(json.dumps(data, cls=MyEncoder), object_hook=decoder)

CSV ファイルの読み込み

Windows で空行問題を回避するため、常に newline='' で CSV ファイルを開いてください。csv.reader はリストを返し、csv.DictReader はヘッダー行をキーとする dict を返します。csv モジュールは引用符、埋め込みカンマ、改行を正しく処理します — line.split(',') で CSV 行を手動分割しないでください。Sniffer でデリミタを自動検出できます。

python
import csv

# Basic reader: each row is a list of strings
with open("data.csv", newline="", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)  # ['name', 'age', 'city']

# DictReader: each row is a dict keyed by header
with open("data.csv", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row["name"], row["age"])  # access by column name

# Handle different delimiters and quoting
with open("data.tsv", newline="") as f:
    reader = csv.reader(f, delimiter="\t", quotechar='"')
    for row in reader:
        print(row)

# Sniffer to auto-detect format
with open("unknown.csv", newline="") as f:
    sample = f.read(1024)
    dialect = csv.Sniffer().sniff(sample)
    f.seek(0)
    reader = csv.reader(f, dialect)

CSV ファイルの書き込み

csv.writer はリストを書き込み、csv.DictWriter は固定のフィールド名セットで dict を書き込みます。ファイルを開く際は常に newline='' を使用してください。quoting パラメータがフィールドを引用符で囲むタイミングを制御します — QUOTE_MINIMAL(デフォルト)は必要な時のみ引用符で囲み、QUOTE_ALL はすべてを引用符で囲み、厳密なパーサーに便利です。

python
import csv

rows = [
    ["name", "age", "city"],
    ["Alice", 30, "NYC"],
    ["Bob", 25, "LA"],
]

# Basic writer
with open("out.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(rows)  # write multiple rows

# DictWriter: write from dicts
with open("out.csv", "w", newline="") as f:
    fieldnames = ["name", "age", "city"]
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({"name": "Alice", "age": 30, "city": "NYC"})
    writer.writerow({"name": "Bob", "age": 25, "city": "LA"})

# Control quoting: QUOTE_MINIMAL (default), QUOTE_ALL, QUOTE_NONNUMERIC
writer = csv.writer(f, quoting=csv.QUOTE_ALL)
# QUOTE_ALL wraps every field in quotes: "Alice","30","NYC"

JSON Lines(NDJSON)とストリーミング

JSON Lines(NDJSON)は 1 行に 1 つの JSON オブジェクトを置きます — 各行を独立して処理できるため、ログ、イベントストリーム、追記専用ファイルに理想的です。巨大な単一 JSON ドキュメントには、ijson ライブラリを使用してファイル全体をメモリに読み込まずにストリーム解析してください。NDJSON は多くのデータパイプラインの標準です。

python
import json

# JSON Lines: one JSON object per line (great for logs, big data)
records = [{"id": 1, "msg": "first"}, {"id": 2, "msg": "second"}]

# Write NDJSON
with open("logs.jsonl", "w") as f:
    for rec in records:
        f.write(json.dumps(rec) + "\n")

# Read NDJSON line by line (memory-efficient for huge files)
with open("logs.jsonl", "r") as f:
    for line in f:
        rec = json.loads(line)
        print(rec["id"], rec["msg"])

# Stream large JSON arrays without loading everything into memory
# Use ijson library for streaming parsing of huge JSON files:
# import ijson
# with open("huge.json", "rb") as f:
#     for item in ijson.items(f, "items.item"):
#         process(item)  # one item at a time
22

ロギングとテスト

logging の基礎

logging モジュールは診断出力を出す標準的な方法です — レベル、フォーマット、出力先を制御できるため print() よりはるかに優れています。モジュールごとに logging.getLogger(__name__) を使用して、モジュールごとに冗長性を調整できます。logging.exception() は自動的にトレースバックを含みます。起動時に basicConfig を一度設定してください。

python
import logging

# Basic configuration (call once at program start)
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

# Log levels (severity ascending)
logging.debug("Detailed debug info")     # DEBUG (10)
logging.info("General information")       # INFO (20)
logging.warning("Something unexpected")   # WARNING (30)
logging.error("A real error occurred")    # ERROR (40)
logging.critical("System is down")        # CRITICAL (50)

# Logging exceptions with traceback
try:
    1 / 0
except ZeroDivisionError:
    logging.exception("Division failed")  # includes full traceback

# Get a named logger (best practice per module)
logger = logging.getLogger(__name__)
logger.info("Module-specific log")

ファイルへのロギングと複数ハンドラー

ハンドラーはログレコードを出力先にルーティングします — コンソール、ファイル、ネットワーク、メール。RotatingFileHandler はファイルサイズを制限しバックアップを保持し、無制限のログ増成を防ぎます。各ハンドラーは独自のレベルとフォーマットを持てます(例:詳細ログをファイルに、簡潔なログをコンソールに)。TimedRotatingFileHandler はサイズではなく時間でローテーションします。

python
import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)

# Console handler (INFO and above)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))

# Rotating file handler (DEBUG and above, max 5MB x 3 backups)
file_handler = RotatingFileHandler(
    "app.log", maxBytes=5_000_000, backupCount=3, encoding="utf-8"
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(
    logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s")
)

logger.addHandler(console)
logger.addHandler(file_handler)

logger.debug("debug to file only")
logger.info("info to both console and file")
logger.error("error everywhere")

unittest の基礎

unittest は Python の組み込みテストフレームワーク(xUnit スタイル)です。テストは TestCase を継承するクラスにあります。setUp/tearDown は各テストの前後に実行され、分離を保証します。一般的なアサーション:assertEqual、assertTrue、assertRaises、assertIn。python -m unittest で test_*.py ファイルを自動検出して実行します。

python
import unittest

def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

class TestMath(unittest.TestCase):
    def setUp(self):
        # runs before each test method
        self.data = [1, 2, 3]

    def tearDown(self):
        # runs after each test method
        pass

    def test_add(self):
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(-1, 1), 0)

    def test_add_types(self):
        self.assertEqual(add("a", "b"), "ab")

    def test_divide_by_zero(self):
        with self.assertRaises(ValueError):
            divide(1, 0)

    def test_membership(self):
        self.assertIn(2, self.data)
        self.assertTrue(3 in self.data)

if __name__ == "__main__":
    unittest.main()
# Run: python -m unittest test_file.py -v

pytest の基礎

pytest は最も人気のある Python テストツールです — プレーンな assert 文でリッチな失敗レポートを得られ、ボイラープレートクラスは不要です。pytest.raises はオプションの正規表現マッチングで例外をチェックします。pytest.approx は浮動小数点比較の不正確さを処理します。pip install pytest でインストールし、pytest -v で詳細出力を実行します。

python
# test_math.py — pytest is simpler and more powerful than unittest
# Install: pip install pytest
# Run: pytest -v

def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

# Plain functions, no classes required
def test_add():
    assert add(1, 2) == 3
    assert add(-1, 1) == 0

def test_add_strings():
    assert add("hello", " world") == "hello world"

# Testing exceptions with pytest.raises
import pytest

def test_divide_by_zero():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(1, 0)

# Approximate float comparison
def test_float():
    assert 0.1 + 0.2 == pytest.approx(0.3)

pytest フィクスチャ

フィクスチャは pytest の依存性注入です — パラメータ名を介してテストにセットアップデータ、モックオブジェクト、リソースを提供します。yield ベースのフィクスチャはセットアップ(yield 前)とティアダウン(yield 後)の両方を処理します。スコープが再利用を制御します:'session' は実行全体で 1 回、'module' はファイルごとに 1 回、'function'(デフォルト)はテストごとに 1 回作成します。

python
import pytest

# A fixture provides setup data/resources to tests
@pytest.fixture
def sample_list():
    return [1, 2, 3, 4, 5]

# Use fixtures by passing their name as a parameter
def test_length(sample_list):
    assert len(sample_list) == 5

def test_sum(sample_list):
    assert sum(sample_list) == 15

# Fixture with setup AND teardown (yield)
@pytest.fixture
def db_connection():
    print("\n[setup] connecting to DB")
    conn = {"connected": True}
    yield conn  # test runs here; value passed to test
    print("\n[teardown] closing DB")
    conn["connected"] = False

def test_db(db_connection):
    assert db_connection["connected"] is True

# Fixture scopes: function (default), class, module, session
@pytest.fixture(scope="session")
def expensive_resource():
    return load_large_dataset()  # created once per test session

pytest parametrize とモック

parametrize は複数の入力セットで単一のテスト関数を実行します — コピーアンドペーストのテストコードを排除し、ケースごとの明確な出力を与えます。unittest.mock.patch は分離テストのために関数/オブジェクトをモックに置き換えます。assert_called_once_with はモックが正しく使用されたことを検証します。@pytest.mark.skip と xfail は不完全なテストを適切に処理します。

python
import pytest
from unittest.mock import patch, MagicMock

# parametrize: run one test with multiple inputs
@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300),
])
def test_add_many(a, b, expected):
    assert add(a, b) == expected

# parametrize with IDs for readable output
@pytest.mark.parametrize("x", [1, 2, 3], ids=["one", "two", "three"])
def test_ids(x):
    assert x > 0

# Mocking: replace external dependencies
def fetch_user(uid):
    # imagine this calls a real API
    return {"id": uid, "name": "real_user"}

@patch("__main__.fetch_user")
def test_with_mock(mock_fetch):
    mock_fetch.return_value = {"id": 1, "name": "mocked"}
    result = fetch_user(1)
    assert result["name"] == "mocked"
    mock_fetch.assert_called_once_with(1)

# Skip and expected failure
@pytest.mark.skip(reason="not implemented yet")
def test_future():
    pass

@pytest.mark.xfail(reason="known bug #42")
def test_known_bug():
    assert 1 == 2
23

ネットワークプログラミング

TCP サーバー

socket モジュールを使用して TCP サーバーを作成します。bind はソケットをアドレスに関連付け、listen はバックログキューを設定し、accept はクライアントが接続するまでブロックします。ファイル記述子を解放するため、常に接続を閉じてください。

python
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8080))
server.listen(5)
conn, addr = server.accept()
data = conn.recv(1024)
conn.sendall(b'Hello')
conn.close()

TCP クライアント

サーバーに接続する TCP クライアントを作成します。connect は接続を確立し、sendall はすべてのバイトを送信し、recv は指定バイトまで読み取ります。文字列とバイトの変換には encode/decode を使用してください。

python
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 8080))
client.sendall(b'Hello Server')
response = client.recv(1024)
print(response.decode())
client.close()

UDP ソケット

UDP はコネクションレスです:ハンドシェイクなし、配信保証なし。recvfrom はデータと送信者アドレスの両方を返します。UDP には SOCK_DGRAM を使用してください。DNS、ゲーム、リアルタイムストリーミングに理想的です。

python
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('localhost', 9090))
data, addr = sock.recvfrom(1024)
print(f"From {addr}: {data.decode()}")
sock.sendto(b'Reply', addr)

HTTP サーバー

http.server モジュールはシンプルな HTTP サーバーを提供します。BaseHTTPRequestHandler をサブクラス化し、do_GET、do_POST をオーバーライドしてください。開発用のみ;本番には gunicorn を使用してください。

python
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-Type', 'text/html')
        self.end_headers()
        self.wfile.write(b'<h1>Hello</h1>')
HTTPServer(('localhost', 8000), Handler).serve_forever()

ソケットタイムアウト

settimeout はすべてのソケット操作のタイムアウトを設定します。操作がタイムアウトを超えると socket.timeout 例外が発生します。クリーンアップを保証するには try/finally を使用してください。

python
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
try:
    sock.connect(('example.com', 80))
    data = sock.recv(1024)
except socket.timeout:
    print('Connection timed out')
finally:
    sock.close()
24

データベース(SQLite)

テーブル作成

sqlite3 は Python に組み込まれています。connect はデータベースファイルを作成または開きます。CREATE TABLE IF NOT EXISTS はテーブルが存在する場合のエラーを防ぎます。変更を保存するには常に commit を呼び出してください。

python
import sqlite3
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL, email TEXT UNIQUE, age INTEGER)''')
conn.commit()

データ挿入

SQL インジェクションを防ぐため、常にパラメータ化クエリ(? プレースホルダー)を使用してください。lastrowid は自動採番された ID を返します。SQL 値に文字列フォーマットを使用しないでください。

python
cursor.execute(
    'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
    ('Alice', '[email protected]', 30))
conn.commit()
print(f"ID: {cursor.lastrowid}")

データ照会

fetchall はマッチするすべての行をタプルのリストとして返します。fetchone は単一行または None を返します。大きな結果セットには、カーソルを直接反復してください。

python
cursor.execute('SELECT * FROM users WHERE age > ?', (25,))
rows = cursor.fetchall()
for row in rows:
    print(row)
cursor.execute('SELECT * FROM users WHERE id = ?', (1,))
user = cursor.fetchone()

更新と削除

UPDATE は既存の行を変更し、DELETE は削除します。rowcount は影響を受けた行を示します。DELETE には常に WHERE を使用してください。commit が変更を永続化します。

python
cursor.execute('UPDATE users SET age = ? WHERE name = ?', (31, 'Alice'))
cursor.execute('DELETE FROM users WHERE age < ?', (18,))
conn.commit()
print(f"Affected: {cursor.rowcount} rows")

Row Factory

conn をコンテキストマネージャとして使用すると、成功時に自動コミットし、例外時にロールバックします。row_factory = sqlite3.Row で列名によるアクセスが可能になります。

python
conn = sqlite3.connect('example.db')
conn.row_factory = sqlite3.Row
with conn:
    conn.execute('INSERT INTO users (name, email) VALUES (?, ?)',
                 ('Bob', '[email protected]'))
for row in conn.execute('SELECT * FROM users'):
    print(row['name'], row['email'])
25

Web スクレイピング

BeautifulSoup の基礎

requests は HTML コンテンツを取得し、BeautifulSoup が解析します。html.parser は組み込みです;lxml はより高速です。解析前に常に response.status_code を確認してください。

python
import requests
from bs4 import BeautifulSoup
resp = requests.get('https://example.com')
soup = BeautifulSoup(resp.text, 'html.parser')
print(soup.title.string)
print(soup.find('h1').text)

要素の検索

find_all はマッチするすべての要素を返し、find は最初のものを返します。class_(アンダースコア付き)を使用してください。select は複雑なクエリに CSS セレクタを使用します。

python
links = soup.find_all('a')
for link in links:
    print(link.get('href'), link.text)
article = soup.find('div', class_='article')
items = soup.select('ul.list > li.item')

テーブルの抽出

テーブルは td(データ)または th(ヘッダー)セルを含む tr(行)として構造化されています。strip は空白を削除します。find_all はタグ名のリストを受け入れます。

python
table = soup.find('table')
for row in table.find_all('tr'):
    cols = row.find_all(['td', 'th'])
    data = [col.text.strip() for col in cols]
    print(data)

ページネーションの処理

ページネーションは次ページへのリンクをたどることで処理します。select_one は最初のマッチまたは None を返します。リクエスト間に time.sleep を追加してください。

python
all_items = []
url = 'https://example.com/page/1'
while url:
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, 'html.parser')
    all_items.extend([i.text for i in soup.select('.item')])
    next_link = soup.select_one('a.next')
    url = next_link.get('href') if next_link else None

CSV に保存

csv.DictWriter は辞書を CSV に書き込みます。newline は Windows での余分な空行を防ぎます。encoding=utf-8 は特殊文字を処理します。

python
import csv
with open('data.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames=['name', 'price'])
    writer.writeheader()
    for item in scraped_data:
        writer.writerow(item)
26

非同期 Web(aiohttp)

HTTP クライアント

aiohttp は非同期 HTTP を提供します。ClientSession は接続プールを管理します。async with はクリーンアップを保証します。asyncio.run はコルーチンを実行します。

python
import aiohttp, asyncio
async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.text()
data = asyncio.run(fetch('https://api.example.com'))

並行リクエスト

asyncio.gather はコルーチンを並行実行し、合計時間を短縮します。すべてのリクエストは同じセッションを共有します。並行数を制限するにはセマフォを使用してください。

python
async def fetch_all(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [session.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
        return [await r.text() for r in responses]

Web サーバー

aiohttp.web は非同期 Web サーバーを作成します。ルートは HTTP メソッドとパスパターンで定義されます。match_info はパスパラメータを抽出します。

python
from aiohttp import web
async def handle(request):
    name = request.match_info.get('name', 'World')
    return web.json_response({'message': f'Hello, {name}!'})
app = web.Application()
app.add_routes([web.get('/', handle), web.get('/{name}', handle)])
web.run_app(app, port=8080)

WebSocket サーバー

WebSocket は双方向のリアルタイム通信を可能にします。WebSocketResponse はアップグレードハンドシェイクを処理します。async for はメッセージを反復します。

python
async def ws_handler(request):
    ws = web.WebSocketResponse()
    await ws.prepare(request)
    async for msg in ws:
        if msg.type == aiohttp.WSMsgType.TEXT:
            await ws.send_str(f'Echo: {msg.data}')
    return ws

Cookie を伴うセッション

ClientSession はリクエスト間で Cookie を自動的に永続化します。認証付きスクレイピングに不可欠です。全リクエストに単一のセッションを使用してください。

python
async def login_and_fetch():
    async with aiohttp.ClientSession() as session:
        await session.post('https://example.com/login',
                          data={'user': 'admin', 'pass': '123'})
        resp = await session.get('https://example.com/dashboard')
        return await resp.text()
27

マルチプロセスの詳細

プロセスプール

Pool はワーカープロセスを管理します。map は作業を並列に分散します。apply_async は単一の関数を非同期に実行します。Windows では常に if __name__ == main ガードを使用してください。

python
from multiprocessing import Pool
def square(x): return x * x
if __name__ == '__main__':
    with Pool(4) as pool:
        results = pool.map(square, range(10))
        result = pool.apply_async(square, (100,))
        print(result.get(timeout=5))

共有メモリ

Value と Array はプロセス間で共有メモリを作成します。競合状態を防ぐため get_lock でアクセスを同期してください。

python
from multiprocessing import Value, Array
counter = Value('i', 0)
arr = Array('d', [0.0, 1.0, 2.0])
with counter.get_lock():
    counter.value += 1

Queue 通信

Queue はプロセス間の安全な通信を可能にします。put は項目を追加し、get は取得します。Queue はプロセスセーフで、内部的にロックを処理します。

python
from multiprocessing import Process, Queue
def worker(q):
    q.put('Data from worker')
if __name__ == '__main__':
    q = Queue()
    p = Process(target=worker, args=(q,))
    p.start()
    print(q.get())
    p.join()

Pipe

Pipe は双方向通信チャネルを作成します。send と recv はピクル化を介して Python オブジェクトを送信します。ポイントツーポイント通信では Queue より高速です。

python
from multiprocessing import Process, Pipe
def worker(conn):
    conn.send(['hello', 'world'])
    msg = conn.recv()
    conn.close()
if __name__ == '__main__':
    parent, child = Pipe()
    p = Process(target=worker, args=(child,))
    p.start()
    print(parent.recv())
    parent.send('acknowledged')
    p.join()

同期

Lock は一度に 1 つのプロセスのみが共有リソースにアクセスすることを保証します。with lock で自動的に取得・解放します。その他のプリミティブ:RLock、Semaphore、Event。

python
from multiprocessing import Process, Lock
def safe_print(lock, msg):
    with lock:
        print(msg)
if __name__ == '__main__':
    lock = Lock()
    procs = [Process(target=safe_print, args=(lock, f'Task {i}'))
             for i in range(5)]
    for p in procs: p.start()
    for p in procs: p.join()
28

仮想環境の詳細

venv モジュール

venv は独自のパッケージディレクトリを持つ隔離された Python 環境を作成します。アクティベート時に PATH を変更します。依存関係をインストールする前に必ずアクティベートしてください。

python
# Create
python -m venv myenv
# Activate (Linux/Mac)
source myenv/bin/activate
# Activate (Windows)
myenv\Scripts\activate
# Deactivate
deactivate

requirements.txt

requirements.txt はプロジェクトの依存関係をリストします。== は正確なバージョンを固定し、>= は範囲内でのアップグレードを許可します。必ずバージョン管理にコミットしてください。

python
# Generate
pip freeze > requirements.txt
# Install
pip install -r requirements.txt
# Pin versions
flask==2.3.3
requests>=2.28.0,<3.0.0

Poetry

Poetry はモダンな依存関係マネージャです。pyproject.toml が requirements.txt を置き換えます。仮想環境は自動的に管理されます。

python
# Initialize
poetry init
# Add dependency
poetry add flask
poetry add pytest --group dev
# Install all
poetry install
# Run command
poetry run python app.py

pipenv

pipenv は pip と virtualenv を組み合わせます。Pipfile が依存関係を宣言し、Pipfile.lock が正確なバージョンを固定します。--dev は開発用依存関係を分離します。

python
# Create environment
pipenv install
# Add package
pipenv install requests
pipenv install pytest --dev
# Activate shell
pipenv shell
# Run command
pipenv run python app.py

Conda 環境

Conda は Python と非 Python の依存関係の両方を管理します。environment.yml が完全な環境を記録します。バイナリ依存関係があるデータサイエンスに理想的です。

python
# Create
conda create -n myenv python=3.11
# Activate
conda activate myenv
# Export
conda env export > environment.yml
# Recreate
conda env create -f environment.yml
29

pip の高度な使用法

Git からインストール

Git リポジトリから直接パッケージをインストールします。未リリースのバージョン、フォーク、プライベートパッケージに便利です。@branch または @commit で特定のバージョンを固定します。

python
# From GitHub
pip install git+https://github.com/user/repo.git
# Specific branch
pip install git+https://github.com/user/repo.git@branch-name
# Specific commit
pip install git+https://github.com/user/repo.git@abc123

編集可能インストール

編集可能インストール(-e)はパッケージをコピーする代わりにリンクします。変更は再インストールなしで即座に利用可能になります。パッケージ開発に不可欠です。

python
# Install in development mode
pip install -e .
# From a specific path
pip install -e /path/to/package
# With extras
pip install -e ".[dev,test]"

制約とハッシュ

制約はインストール可能なバージョンを制限します。ハッシュチェックはパッケージの完全性を検証し、サプライチェーン攻撃を防ぎます。

python
# constraints.txt
flask==2.3.3
pip install -c constraints.txt flask
# Hash checking
pip install --require-hashes -r requirements.txt

キャッシュ管理

pip はダウンロードした wheel をキャッシュします。--no-cache-dir は強制的に新規ダウンロードします。キャッシュが大きくなりすぎた場合はパージでディスク容量を解放します。

python
# Show cache info
pip cache info
# List cached packages
pip cache list
# Purge entire cache
pip cache purge
# Install with no cache
pip install --no-cache-dir flask

カスタムインデックス

--index-url はカスタムパッケージリポジトリを指定します。--extra-index-url はフォールバックを追加します。--trusted-host は内部レジストリの SSL をバイパスします。

python
# Use custom index
pip install --index-url https://pypi.custom.com/simple/ flask
# Extra index (fallback)
pip install --extra-index-url https://pypi.custom.com/simple/ flask
# Trusted host (no SSL)
pip install --trusted-host pypi.custom.com flask
30

型チェック (mypy)

基本的な型ヒント

型ヒントは関数のパラメータと戻り値の型をアノテーションします。Python 3.9+ では組み込み型を直接使用できます。ヒントにより mypy での静的解析が可能になります。

python
def greet(name: str, times: int = 1) -> str:
    return (f"Hello, {name}! " * times).strip()
def process(data: list[int]) -> dict[str, int]:
    return {str(x): x for x in data}

Optional と Union

Optional[X] は X | None と同等です(Python 3.10+)。Union 型は複数の可能な型を許可します。mypy はすべてのコードパスがすべての型を処理することをチェックします。

python
from typing import Optional
def find(items: list[int], target: int) -> int | None:
    for i, v in enumerate(items):
        if v == target: return i
    return None

ジェネリック型

ジェネリクスは再利用可能な型セーフなコンテナを作成します。TypeVar は型変数を定義し、Generic はクラスをジェネリックにします。mypy は型の一貫性を保証します。

python
from typing import TypeVar, Generic
T = TypeVar('T')
class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []
    def push(self, item: T) -> None:
        self._items.append(item)
    def pop(self) -> T:
        return self._items.pop()

Protocol

Protocol は構造的サブタイピング(型チェック付きダックタイピング)を定義します。必要なメソッドを持つ任意のクラスがプロトコルを満たし、継承は不要です。

python
from typing import Protocol
class Closeable(Protocol):
    def close(self) -> None: ...
def cleanup(resource: Closeable) -> None:
    resource.close()
class File:
    def close(self) -> None: print("Closed")
cleanup(File())  # OK - File has close()

mypy 設定

mypy.ini は型チェックの厳密さを設定します。strict はすべてのチェックを有効にします。モジュールごとのオーバーライドでテストやレガシーコードのルールを緩和できます。

python
# mypy.ini
[mypy]
python_version = 3.11
strict = True
warn_return_any = True
disallow_untyped_defs = True
[mypy-tests.*]
ignore_errors = True
31

パフォーマンスのヒント

リスト vs ジェネレータ

リストはすべての要素をメモリに格納し、ジェネレータは要求に応じて値を生成します。一度だけ反復する大きなシーケンスにはジェネレータを使用してください。

python
# List: all in memory
squares = [x**2 for x in range(1000000)]
# Generator: lazy evaluation
squares_gen = (x**2 for x in range(1000000))
import sys
print(sys.getsizeof(squares))      # ~8MB
print(sys.getsizeof(squares_gen))  # ~200 bytes

文字列連結

+= による文字列連結は O(n^2) です。join は O(n) です。f-strings が最速の補間方法です。

python
# Slow: creates intermediate strings
result = ""
for s in parts:
    result += s
# Fast: join in one operation
result = "".join(parts)
# Fast: f-strings
msg = f"Hello, {name}!"

ローカル変数

ローカル変数のルックアップはグローバルや属性のルックアップより高速です。頻繁に使用する関数をローカルに代入するとループが高速化します。

python
import math
# Slow: global lookup
def compute_slow(values):
    return [math.sqrt(v) for v in values]
# Fast: local reference
def compute_fast(values):
    sqrt = math.sqrt
    return [sqrt(v) for v in values]

__slots__

__slots__ は __dict__ の作成を防ぎ、インスタンスごとのメモリを 40-50% 節約します。数百万のオブジェクトを作成する場合に効果的です。リストされていない属性は追加できません。

python
class Point:
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y
p = Point(1, 2)
# p.z = 3  # AttributeError

timeit と cProfile

timeit は小さなスニペットの実行時間を測定します。cProfile は時間がどこで費やされているかを示します。最適化の前にプロファイリングを使用して実際のボトルネックを見つけてください。

python
import timeit
t = timeit.timeit('sum(range(100))', number=10000)
print(f"{t:.4f}s")
import cProfile
cProfile.run('sum(x**2 for x in range(10000))')
32

よくある落とし穴

ミュータブルなデフォルト引数

デフォルト引数値は定義時に一度評価されます。ミュータブルなデフォルトはすべての呼び出しで共有されます。常に None をデフォルトとして使用してください。

python
# BUG: default list is shared
def add_item(item, lst=[]):
    lst.append(item)
    return lst
print(add_item(1))  # [1]
print(add_item(2))  # [1, 2]!
# FIX: use None
def add_item(item, lst=None):
    if lst is None: lst = []
    lst.append(item)
    return lst

遅延バインディングクロージャ

クロージャは参照によって変数をキャプチャします。lambda が呼び出される時点で、ループ変数は最終値を持っています。デフォルト引数で現在の値をキャプチャします。

python
# BUG: all print 2
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs])  # [2, 2, 2]
# FIX: default argument
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs])  # [0, 1, 2]

整数キャッシュ

Python は小さな整数をキャッシュします。is は同一性をチェックし、== は等価性をチェックします。値の比較に is を使用しないでください。is は None、True、False にのみ使用してください。

python
# Small integers cached (-5 to 256)
a = 256; b = 256
print(a is b)  # True (cached)
c = 257; d = 257
print(c is d)  # False (not cached)
print(c == d)  # True

is と ==

is は 2 つの参照が同じオブジェクトを指すかをチェックします。== は 2 つのオブジェクトが同じ値を持つかをチェックします。is は None、True、False にのみ使用してください。

python
a = [1, 2, 3]; b = [1, 2, 3]
print(a == b)  # True (same values)
print(a is b)  # False (different objects)
# Correct usage of is
if x is None: ...
if x is not None: ...

GIL

GIL は一度に 1 つのスレッドのみが Python バイトコードを実行することを許可します。スレッドは I/O バウンドのタスクに効果的です。CPU バウンドの並列処理には multiprocessing を使用してください。

python
# GIL prevents true parallelism for CPU-bound tasks
import threading
def cpu_work():
    total = sum(i**2 for i in range(10**7))
# Use multiprocessing for CPU work
from multiprocessing import Pool
with Pool(4) as p:
    p.map(cpu_work, range(4))

関連する Python スニペット

Copy-paste ready code for common tasks.

値で辞書をソート

Python の辞書を値で降順にソート。

リスト内包表記

リスト内包表記でリストを素早く生成。

辞書のマージ

辞書をマージする複数の方法。

ファイル読み書き

ファイルの読み書きのさまざまな方法。

CSV 処理

csv モジュールで CSV ファイルの読み書き。

JSON 処理

JSON のシリアライズとデシリアライズ。

正規表現マッチング

re モジュールで正規表現マッチングを実行。

日付処理

datetime で日付と時刻を処理。

デコレータ

デコレータの定義と使用。

ジェネレータ

ジェネレータでメモリを節約。

コンテキストマネージャ

カスタムコンテキストマネージャ。

例外処理

完全な例外処理メカニズム。

クラスの継承

クラスの継承とメソッドのオーバーライド。

マルチスレッド

threading モジュールでマルチスレッドを実装。

マルチプロセス

multiprocessing で真の並列処理を実現。

asyncio 非同期プログラミング

asyncio で非同期並行処理を実装。

Socket プログラミング

TCP Socket サーバーとクライアント。

HTTP リクエスト

requests ライブラリで HTTP リクエストを送信。

データベース操作

sqlite3 でデータベースを操作。

仮想環境

Python の仮想環境を作成・管理。

pip インストール

一般的な pip パッケージ管理コマンド。

環境変数

環境変数の読み取りと設定。

ロギング

logging モジュールの設定と使用。

ユニットテスト

unittest でユニットテストを記述。

型ヒント

型アノテーションでコードの可読性を向上。

Dataclass

dataclass でクラス定義を簡素化。

Enum

Enum で列挙型を定義。

プロパティデコレータ

property で属性アクセスを制御。

マジックメソッド

一般的なマジックメソッドの例。

イテレータ

カスタムイテレータの実装。

コルーチン

コルーチンの基本的な使い方。

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.