Computer368106 views
高校倫理1440106 views
高校物理160203 views
数学講師2886227 views
小学算数1200427 views
教育149510 views
高校日本史190571 views
いろは3010814 views
中学英語811710 views
高校化学2924392 views
Help
Tools
NewsSpreadsheetCalendarSlidesTier ListPen ToolIllustrationCrayonPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsCross SectionsMotion PathMechanicsWavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic Theory

English

make_dataclass で動的にクラスを生成する|Python

dataclass は通常、デコレータを使ってクラス定義時に静的に作成する。しかし、フィールド構成が実行時まで確定しない場面もある。CSV のヘッダーから自動でクラスを作りたい、設定ファイルに応じてフィールドを変えたい、といったケースだ。make_dataclass はそうした動的なクラス生成を可能にする関数である。

make_dataclass の基本

dataclasses.make_dataclass は、クラス名とフィールドのリストを受け取り、dataclass を返す。

from dataclasses import make_dataclass

Point = make_dataclass("Point", ["x", "y"])

p = Point(x=1.0, y=2.0)
print(p)       # Point(x=1.0, y=2.0)
print(p.x)     # 1.0

フィールドをリストで渡すだけで、@dataclass を付けたクラスと同等のものが生成される。__init____repr____eq__ などのメソッドもすべて自動で作られる。

これは以下のコードとほぼ同じ結果になる。

from dataclasses import dataclass

@dataclass
class Point:
    x: ...
    y: ...

違いは、make_dataclass ではクラス名もフィールドも文字列として渡せる点だ。つまり、プログラムの実行中に決まる情報からクラスを組み立てられる。

フィールドに型を指定する

フィールド名だけを渡すと型は typing.Any になる。型を明示するには、タプルで (名前, 型) を渡す。

from dataclasses import make_dataclass

User = make_dataclass("User", [
    ("name", str),
    ("age", int),
    ("score", float),
])

u = User(name="Alice", age=30, score=95.5)
print(u)  # User(name='Alice', age=30, score=95.5)

型ヒントとして記録されるだけで、実行時の型チェックが走るわけではない。これは通常の dataclass と同じ挙動だ。

デフォルト値を設定する

3 要素のタプル (名前, 型, デフォルト値) でデフォルト値を指定できる。field() も使える。

from dataclasses import make_dataclass, field

Config = make_dataclass("Config", [
    ("host", str, "localhost"),
    ("port", int, 8080),
    ("tags", list, field(default_factory=list)),
])

c = Config()
print(c)  # Config(host='localhost', port=8080, tags=[])

通常の dataclass と同じく、デフォルト値のないフィールドはデフォルト値のあるフィールドより前に置かなければならない。順序を間違えると TypeError になる。

from dataclasses import make_dataclass

# これはエラーになる
Bad = make_dataclass("Bad", [
    ("name", str, "default"),  # デフォルトあり
    ("age", int),              # デフォルトなし → TypeError
])

オプションの指定

make_dataclass@dataclass デコレータと同じオプションを受け取れる。frozenslotsorder などをキーワード引数で渡す。

from dataclasses import make_dataclass

FrozenPoint = make_dataclass(
    "FrozenPoint",
    [("x", float), ("y", float)],
    frozen=True,
    slots=True,
)

p = FrozenPoint(x=1.0, y=2.0)
print(hash(p))   # ハッシュ可能
# p.x = 3.0      # FrozenInstanceError
@dataclass デコレータ

ソースコード上でクラスを定義する。フィールド構成はコードに固定される。可読性が高く、IDE の補完も効く。

make_dataclass 関数

実行時にクラス名とフィールドを組み立てる。動的な生成が可能だが、IDE の型補完は効きにくい。

静的に定義できるなら @dataclass を使うべきだ。make_dataclass は「静的に書けない場面」のためのツールである。

CSV ヘッダーからクラスを生成する

make_dataclass の実用的な使い方として、CSV ファイルのヘッダーからデータクラスを自動生成するパターンがある。

import csv
from dataclasses import make_dataclass

csv_text = """name,age,city
Alice,30,Tokyo
Bob,25,Osaka
Charlie,35,Nagoya"""

reader = csv.reader(csv_text.strip().splitlines())
headers = next(reader)

Row = make_dataclass("Row", [(h, str) for h in headers])

rows = [Row(*values) for values in reader]
for row in rows:
    print(row)

# Row(name='Alice', age='30', city='Tokyo')
# Row(name='Bob', age='25', city='Osaka')
# Row(name='Charlie', age='35', city='Nagoya')

ヘッダーが変わってもコードを書き換える必要がない。列の追加や削除に自動で対応できるため、汎用的なデータ読み込みツールに向いている。

辞書からクラスを生成する

API レスポンスや設定ファイルの辞書から dataclass を作るパターンも有用だ。

from dataclasses import make_dataclass

def dict_to_dataclass(name, data):
    fields = [(k, type(v), v) for k, v in data.items()]
    cls = make_dataclass(name, fields)
    return cls

schema = {"host": "localhost", "port": 8080, "debug": False}
Config = dict_to_dataclass("Config", schema)

c = Config()
print(c)  # Config(host='localhost', port=8080, debug=False)

c2 = Config(host="0.0.0.0", port=3000, debug=True)
print(c2)  # Config(host='0.0.0.0', port=3000, debug=True)

辞書の値から型を推論し、同時にデフォルト値として設定している。簡易的なアプローチだが、設定ファイルのパースなどでは十分に実用的だ。

基底クラスの指定

bases 引数で親クラスを指定できる。既存のクラスを継承した dataclass を動的に作れる。

from dataclasses import make_dataclass, dataclass

@dataclass
class Base:
    id: int

Extended = make_dataclass(
    "Extended",
    [("name", str), ("value", float)],
    bases=(Base,),
)

e = Extended(id=1, name="test", value=3.14)
print(e)              # Extended(id=1, name='test', value=3.14)
print(isinstance(e, Base))  # True

既存の型階層に動的なクラスを組み込めるため、プラグインシステムやスキーマ駆動のコード生成で活用できる。

名前空間にメソッドを追加する

namespace 引数を使うと、生成されるクラスにメソッドやプロパティを追加できる。

from dataclasses import make_dataclass
import math

Circle = make_dataclass(
    "Circle",
    [("radius", float)],
    namespace={
        "area": property(lambda self: math.pi * self.radius ** 2),
        "circumference": property(lambda self: 2 * math.pi * self.radius),
    },
)

c = Circle(radius=5.0)
print(f"面積: {c.area:.2f}")      # 面積: 78.54
print(f"周長: {c.circumference:.2f}")  # 周長: 31.42

ただし、namespace に多数のメソッドを詰め込むなら、通常の @dataclass で静的にクラスを定義するほうが可読性は高い。namespace は補助的な用途に留めるのが賢明だ。

make_dataclass でフィールドにデフォルト値を設定するとき、正しい書き方はどれか?

  • ("name", "default", str)
  • ("name", str, "default")
  • {"name": str, "default": "value"}
  • field("name", default="value")
__RESULT__

フィールドは (名前, 型, デフォルト値) の 3 要素タプルで指定します。順番は名前 → 型 → デフォルト値です。

make_dataclass の限界

動的生成は強力だが、トレードオフも明確にある。

型チェッカーとの相性

mypy や pyright は make_dataclass で生成したクラスの型情報を追跡できない。静的解析の恩恵を受けられないため、大規模プロジェクトでは型安全性が低下する。

可読性の低下

どんなフィールドを持つクラスなのかがソースコードを読むだけではわからない。デバッグ時にクラス定義を追いかけるのが難しくなる。

IDE サポートの不足

コード補完やリファクタリング支援が効かない。属性名のタイポに気づきにくく、開発効率に影響する。

設計時にフィールドが確定しているなら、迷わず @dataclass を使うべきだ。make_dataclass は、外部データやメタ情報に基づいてクラスを生成する必要がある場面に限定して使うのが適切である。動的生成の便利さと引き換えに失うものを理解した上で、意図的に選択することが重要だ。