Поля с ошибками в классе Python @dataclass
Как заставить это поднять исключение при установке полей с ошибками в @dataclass
класс Python?
Я хочу практичный способ сделать это. Нужно ли мне писать свой собственный декоратор?
@dataclass
class C(object):
x: int = 1
obj = C()
obj.y = 2 # should raise an exception
3 ответа
Одним простым способом (который работает с любым классом) является определение __slots__
:
In [1]: from dataclasses import dataclass
In [2]: @dataclass
...: class Foo:
...: __slots__ = 'bar','baz'
...: bar: int
...: baz: int
...:
In [3]: foo = Foo(42, 88)
In [4]: foo.biz = 10
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-d52b60444257> in <module>()
----> 1 foo.biz = 10
AttributeError: 'Foo' object has no attribute 'biz'
Целью слотов является небольшая оптимизация. Это позволяет экземплярам класса использовать таблицу символов вместо dict
в качестве пространства имен класса. Это немного увеличивает скорость доступа к атрибутам и может значительно улучшить использование памяти для каждого экземпляра (поскольку экземпляр не переносит dict
под капотом), однако, он запрещает динамическую настройку атрибутов.
Это на самом деле моя любимая особенность __slots__
,
Обратите внимание, что вы должны соблюдать осторожность при использовании наследования со слотами, по крайней мере, если вы хотите, чтобы подклассы сохраняли поведение слотов.
Один из способов сделать это - пометить класс данных как frozen
:
>>> from dataclasses import dataclass
>>> @dataclass(frozen=True)
... class C:
... x: int = 1
...
>>> c = C()
>>> c.y = 1
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'y'
Но обратите внимание, что это делает все существующие атрибуты, например x
в этом случае тоже только для чтения.
Вы можете предоставить свой собственный
__setattr__()
метод, который позволяет присваивать только известные поля.
Пример:
@dataclass
class C:
x: int = 1
def __setattr__(self, k, v):
if k not in self.__annotations__:
raise AttributeError(f'{self.__class__.__name__} dataclass has no field {k}')
super().__setattr__(k, v)
Тогда не работает / работает так:
[ins] In [3]: obj = C()
...: obj.y = 2
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-7a568eb098b1> in <module>
1 obj = C()
----> 2 obj.y = 2
<ipython-input-2-d30972f86fbb> in __setattr__(self, k, v)
5 def __setattr__(self, k, v):
6 if k not in self.__annotations__:
----> 7 raise AttributeError(f'{self.__class__.__name__} dataclass has no field {k}')
8 super().__setattr__(k, v)
9
AttributeError: C dataclass has no field y
[ins] In [4]: obj.x = 23
[ins] In [5]: obj.x
Out[5]: 23