コピペで使える Python 実践 Tips 集【中級者向け】

Python コピペで即使えるテクニックを、実務経験者が「もっと早く知りたかった」と感じる観点で厳選しました。Python 3.11 以降を前提にしています。

内包表記・ジェネレータ式を使いこなす

リスト内包表記でフィルタと変換を一行で

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

evens_squared = [x**2 for x in numbers if x % 2 == 0]
# [4, 16, 36, 64, 100]

ネストしたリストの平坦化も内包表記で書けます。

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [cell for row in matrix for cell in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

辞書内包表記でキーと値を変換する

prices = {"apple": 100, "banana": 200, "cherry": 300}

discounted = {k: int(v * 0.9) for k, v in prices.items()}
# {"apple": 90, "banana": 180, "cherry": 270}

inverted = {v: k for k, v in prices.items()}
# {100: "apple", 200: "banana", 300: "cherry"}

ジェネレータ式でメモリを節約する

大量データを扱うときは [] の代わりに () でジェネレータにします。

import sys

data = range(1_000_000)

list_mem = sys.getsizeof([x**2 for x in data])
gen_mem = sys.getsizeof(x**2 for x in data)

print(list_mem)  # 約 8 MB
print(gen_mem)   # 约 200 B

summaxanyall はジェネレータを直接受け取れます。

total = sum(x**2 for x in range(1000))
has_negative = any(x < 0 for x in [1, -2, 3])

dataclass / Pydantic でデータ定義をシンプルに

dataclass で軽量なデータクラスを定義する

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class Article:
    title: str
    author: str
    tags: list[str] = field(default_factory=list)
    published_at: datetime = field(default_factory=datetime.now)
    view_count: int = 0

    def is_popular(self) -> bool:
        return self.view_count >= 1000

article = Article(title="Python Tips", author="Alice", tags=["python"])
print(article.is_popular())  # False

frozen=True を付けると不変オブジェクトになります。

@dataclass(frozen=True)
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
# p.x = 3.0  # FrozenInstanceError

Pydantic でバリデーション付きモデルを作る

from pydantic import BaseModel, EmailStr, field_validator

class User(BaseModel):
    name: str
    email: str
    age: int

    @field_validator("age")
    @classmethod
    def age_must_be_positive(cls, v: int) -> int:
        if v <= 0:
            raise ValueError("age must be positive")
        return v

user = User(name="Bob", email="bob@example.com", age=30)
print(user.model_dump())
# {"name": "Bob", "email": "bob@example.com", "age": 30}

外部 API レスポンスのパースには model_validate が便利です。

raw = {"name": "Carol", "email": "carol@example.com", "age": "25"}
user = User.model_validate(raw)  # age が str → int に自動変換

型ヒントを活用する(TypedDict・Protocol・Literal)

TypedDict で辞書の形を型で表現する

from typing import TypedDict, NotRequired

class Config(TypedDict):
    host: str
    port: int
    debug: NotRequired[bool]

def connect(config: Config) -> None:
    print(f"Connecting to {config['host']}:{config['port']}")

connect({"host": "localhost", "port": 5432})

Protocol で構造的部分型(duck typing)を型安全に書く

from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

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

class Square:
    def draw(self) -> None:
        print("Drawing square")

def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())
render(Square())

Protocol を使うと継承なしで型チェックが通ります。

Literal で取りうる値を絞り込む

from typing import Literal

Direction = Literal["north", "south", "east", "west"]
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR"]

def move(direction: Direction, steps: int) -> None:
    print(f"Moving {direction} by {steps} steps")

def log(level: LogLevel, message: str) -> None:
    print(f"[{level}] {message}")

move("north", 3)
log("INFO", "Server started")

pathlib でファイル操作をスッキリ書く

基本的なパス操作

from pathlib import Path

base = Path(".")
config_file = base / "config" / "settings.toml"

print(config_file.name)       # settings.toml
print(config_file.stem)       # settings
print(config_file.suffix)     # .toml
print(config_file.parent)     # config
print(config_file.resolve())  # 絶対パス

ファイル読み書きと glob

from pathlib import Path

log_dir = Path("/var/log/myapp")

# 再帰的にすべての .log ファイルを取得
for log_file in log_dir.rglob("*.log"):
    content = log_file.read_text(encoding="utf-8")
    compressed = log_file.with_suffix(".log.gz")
    print(f"{log_file} -> {compressed}")

# テキスト書き込み(上書き)
output = Path("/tmp/result.txt")
output.write_text("done\n", encoding="utf-8")

# 追記
with output.open("a", encoding="utf-8") as f:
    f.write("appended line\n")

ディレクトリの作成・存在確認

from pathlib import Path

work_dir = Path("/tmp/myapp/cache")
work_dir.mkdir(parents=True, exist_ok=True)

if (work_dir / "data.json").exists():
    print("cache hit")
else:
    print("cache miss")

contextlib で with 文をカスタム実装する

contextmanager デコレータで簡潔に書く

from contextlib import contextmanager
import time

@contextmanager
def timer(label: str):
    start = time.perf_counter()
    try:
        yield
    finally:
        elapsed = time.perf_counter() - start
        print(f"{label}: {elapsed:.4f}s")

with timer("heavy computation"):
    total = sum(range(10_000_000))

suppress で特定の例外を無視する

from contextlib import suppress
from pathlib import Path

with suppress(FileNotFoundError):
    Path("/tmp/maybe_exists.txt").unlink()

ExitStack で動的に複数の with ブロックを管理する

from contextlib import ExitStack
from pathlib import Path

files = [Path(f"/tmp/file_{i}.txt") for i in range(3)]

with ExitStack() as stack:
    handles = [
        stack.enter_context(f.open("w", encoding="utf-8"))
        for f in files
    ]
    for i, fh in enumerate(handles):
        fh.write(f"file {i}\n")

itertools / functools の便利関数

itertools:イテレータを組み合わせる

import itertools

# islice:ジェネレータから先頭 N 件だけ取る
first_five = list(itertools.islice(range(1_000_000), 5))
# [0, 1, 2, 3, 4]

# chain:複数のイテラブルを連結する
combined = list(itertools.chain([1, 2], [3, 4], [5]))
# [1, 2, 3, 4, 5]

# groupby:連続するキーでグループ化(事前ソート必須)
data = [
    {"dept": "dev", "name": "Alice"},
    {"dept": "dev", "name": "Bob"},
    {"dept": "qa", "name": "Carol"},
]
data.sort(key=lambda x: x["dept"])
for dept, members in itertools.groupby(data, key=lambda x: x["dept"]):
    print(dept, list(members))

# batched(Python 3.12+):N 件ずつのバッチに分割
for batch in itertools.batched(range(10), 3):
    print(list(batch))
# [0, 1, 2], [3, 4, 5], [6, 7, 8], [9]

functools:関数を高次元に扱う

import functools

# lru_cache:メモ化キャッシュ
@functools.lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # 高速

# partial:引数を部分適用する
def power(base: float, exp: float) -> float:
    return base ** exp

square = functools.partial(power, exp=2)
cube   = functools.partial(power, exp=3)
print(square(5))  # 25.0
print(cube(3))    # 27.0

# reduce:畳み込み演算
product = functools.reduce(lambda acc, x: acc * x, [1, 2, 3, 4, 5])
# 120

cache と cached_property

import functools

# cache(Python 3.9+):引数制限なしの lru_cache
@functools.cache
def heavy_calc(n: int) -> int:
    return sum(range(n))

# cached_property:インスタンスメソッドを初回呼び出し時だけ計算してキャッシュ
class DataProcessor:
    def __init__(self, raw: list[int]):
        self.raw = raw

    @functools.cached_property
    def statistics(self) -> dict[str, float]:
        return {
            "mean": sum(self.raw) / len(self.raw),
            "max": max(self.raw),
            "min": min(self.raw),
        }

proc = DataProcessor([10, 20, 30, 40])
print(proc.statistics)  # 初回のみ計算
print(proc.statistics)  # キャッシュから返る

まとめ

今回紹介した Python コピペ Tips をカテゴリ別にまとめます。

カテゴリ主なポイント
内包表記・ジェネレータリスト/辞書/セット内包表記、ジェネレータ式によるメモリ節約
dataclass / Pydantic@dataclass で軽量モデル、Pydantic でバリデーション付きモデル
型ヒントTypedDict で辞書型定義、Protocol で構造的部分型、Literal で値を絞り込み
pathlibPath によるパス結合・glob・読み書きをOS非依存で記述
contextlib@contextmanager でカスタム with 文、suppress で例外無視、ExitStack で動的管理
itertoolsislicechaingroupbybatched でイテレータを合成
functoolslru_cache/cache でメモ化、partial で部分適用、cached_property でプロパティキャッシュ

どれも標準ライブラリかメジャーなサードパーティで完結するため、依存を増やさずに実務コードをすっきり書けます。ぜひ手元のコードに組み込んでみてください。