dataclassesの_create_fnを完全に理解する
ExampleClassに__setattr__を生やす例
code:exec_example.py
from dataclasses import FrozenInstanceError
class ExampleClass:
...
locals = {"cls": ExampleClass, "FrozenInstanceError": FrozenInstanceError}
local_vars = ", ".join(locals.keys())
name = "__setattr__"
args = ",".join(("self", "name", "value"))
return_annotation = ""
# type(self) is cls と isinstance(self, cls) はどこまで一致するか気になって追加
body = """ print(type(self), type(self) is cls, isinstance(self, cls))
if type(self) is cls or name in ('foo', 'bar',):
raise FrozenInstanceError(f"cannot assign to field {name!r}")
super(cls, self).__setattr__(name, value)"""
txt = f"""def __create_fn__({local_vars}):
def {name}({args}){return_annotation}:
{body}
return {name}
"""
ns = {}
exec(txt, globals(), ns)
setattr(ExampleClass, fn.__name__, fn)
__setattr__の呼び出しもトラックできるようにした
code:結果.python
$ python3.10 -i tmp/exec_example.py
>> ex = ExampleClass()
>> ex.foo = 42 # __setattr__が機能している
<class '__main__.ExampleClass'> True True
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 5, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'foo'
>> ex.hoge = "spam"
<class '__main__.ExampleClass'> True True
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 5, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'hoge'
>> setattr(ex, "foo", 42) # ex.__setattr__ が呼び出されている
<class '__main__.ExampleClass'> True True
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 5, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'foo'
>> object.__setattr__(ex, "foo", 42) # ex.__setattr__ が呼び出されていない!
object.__setattr__はどう動くのか?
組み込みのsetattrと動きが違うのはなぜ?