コピペで使える 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
sum や max、any、all はジェネレータを直接受け取れます。
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 で値を絞り込み |
| pathlib | Path によるパス結合・glob・読み書きをOS非依存で記述 |
| contextlib | @contextmanager でカスタム with 文、suppress で例外無視、ExitStack で動的管理 |
| itertools | islice・chain・groupby・batched でイテレータを合成 |
| functools | lru_cache/cache でメモ化、partial で部分適用、cached_property でプロパティキャッシュ |
どれも標準ライブラリかメジャーなサードパーティで完結するため、依存を増やさずに実務コードをすっきり書けます。ぜひ手元のコードに組み込んでみてください。