1. はじめに
この問題集は、Pythonの基礎を習得した後、次の段階へ進みたい人のサポートをすることが目的です。
また、競技プログラミングとは異なり、複雑なアルゴリズムの問題ではなく「可読性の最大化」に焦点を当てた問題が中心となります。
目次
前の問題:型に厳密なデータクラス1
次の問題:型に厳密なデータクラス3
2. 問題
"""
No.5 型に厳密なデータクラス2
次のSampleデータクラスにはvalueという属性があり、float型を指定しています。
また、__post_init__を活用することでvalue属性の値はfloat型に制限されています。
しかし、Sampleクラスの利用者から次のような要望が出てきました。
全ての要望及び制約を満たすようにSampleクラスの改修案を検討してください。
なお、データクラスの使用は必須ではありません。
1. 再代入したときにも型をチェックしたい
sample = Sample(value=0.0)
sample.value = 'abc'
というように、再代入したときにも型をチェックしたいです。
__post_init__はオブジェクト作成時にしか実行されないため、型チェックが実行されません。
なお再代入時の型チェックロジックは、初期値を指定するときと同じものを使用してください。
制約
* Sampleクラスはvalueという属性を持ち、初期値を与えることができる。
* value属性の初期値にfloat型へ変換できる値が指定された場合、変換後の値をvalue属性に格納する。
* value属性の初期値にfloat型へ変換できない値が指定された場合、エラーを発生させる。
"""
from dataclasses import dataclass
@dataclass
class Sample:
value: float
def __post_init__(self) -> None:
self.value = float(self.value)
3. 解答例
from pydantic import BaseModel, ConfigDict
class Sample(BaseModel):
model_config = ConfigDict(validate_assignment=True)
value: float
4. 採点基準
- 初期値が
float
型へ変換できる場合、value
属性に変換した値が格納されること - 初期値が
float
型へ変換できない場合、エラーが発生すること - 再代入する値が
float
型へ変換できる場合、value
属性に変換した値が格納されること - 再代入する値が
float
型へ変換できない場合、エラーが発生すること
5. 解説
前問の類題です。
今回の問題では、再代入する際にも型チェックをするという条件が追加されています。
5.1 pydanticを使用する場合
まずはpydantic
を使用した場合ですが、解答の通りmodel_config = ...
という1行を追加するだけでOKです。
このmodel_config
変数をConfigDict
で上書きすることで、型チェックの挙動を変更することができます。
型の指定を厳密にするstrict
など、他にも色々なオプションがあるので是非調べてみてください。
Configuration - docs.pydantic.dev
なお、挙動の変更についてもpydantic v1
とpydantic v2
で文法が変更されています。
次のようなConfig
クラスを定義する方法は現在非推奨となっているので注意してください。
from pydantic import BaseModel
class Sample(BaseModel):
value: float
class Config:
validate_assignment = True
FastAPIがPydantic v2対応したので、V2移行のポイントを紹介する(意外と簡単) - Zenn
5.2 データクラスを使用する場合
さて、pydantic
を使った解答は前節の通りですが、データクラスを使った場合はどうなるのでしょうか?
一応、次のようにして実現はできますが……
from dataclasses import dataclass
from typing import Any
@dataclass
class Sample:
value: float
def __setattr__(self, key: str, value: Any) -> None:
if key == 'value':
value = float(value)
super().__setattr__(key, value)
この方法には良くない点があります。
属性value
の名前を変更する場合を考えてみましょう。
最近のエディタは変数名の一括置換ができるものが多いですが、このときif key == 'value'
の'value'
までは置換されません。
変更漏れに気付かなければエラーになってしまいます。
また、これは個人的な感覚になってしまいますが、__setattr__
のオーバーライドは黒魔術と化しやすい印象を持っています。
そのため、この方法は非推奨としたいと思います。
(もしかしたら別の良い方法があるかもしれません)
6. テストコード
print('-----初期化-----')
value = 0.0
sample = Sample(value=value)
assert sample.value == 0.0
assert isinstance(sample.value, float)
value = 0
sample = Sample(value=value)
assert sample.value == 0.0
assert isinstance(sample.value, float)
value = '0.0'
sample = Sample(value=value)
assert sample.value == 0.0
assert isinstance(sample.value, float)
value = 'abc'
try:
_ = Sample(value=value)
except (Exception,) as e:
print(type(e))
print(e)
print('-----再代入-----')
sample = Sample(value=0.0)
new_value = 1.0
sample.value = new_value
assert sample.value == 1.0
assert isinstance(sample.value, float)
sample = Sample(value=0.0)
new_value = 1
sample.value = new_value
assert sample.value == 1.0
assert isinstance(sample.value, float)
sample = Sample(value=0.0)
new_value = '1.0'
sample.value = new_value
assert sample.value == 1.0
assert isinstance(sample.value, float)
sample = Sample(value=0.0)
new_value = 'abc'
try:
sample.value = new_value
except (Exception,) as e:
print(type(e))
print(e)
-----初期化-----
<class 'pydantic_core._pydantic_core.ValidationError'>
1 validation error for Sample
value
Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='abc', input_type=str]
For further information visit https://errors.pydantic.dev/2.11/v/float_parsing
-----再代入-----
<class 'pydantic_core._pydantic_core.ValidationError'>
1 validation error for Sample
value
Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='abc', input_type=str]
For further information visit https://errors.pydantic.dev/2.11/v/float_parsing