python 冻结数据类 object.__setattr__ 不可变
python frozen dataclass immutable with object.__setattr__
在我遇到数据类之前,我将命名元组用于不可变数据结构,我在用例中更喜欢它(与问题无关)。
现在我知道它们不是一成不变的!至少严格来说不是。
setattr(frozen_dc_obj, "prop", "value")
引发异常。好的
但为什么 object.__setattr__(frozen_dc_obj,..)
有效?
与namedtuple
相比,它引发了异常!
from collections import namedtuple
from dataclasses import dataclass
NTTest = namedtuple("NTTest", "id")
nttest = NTTest(1)
setattr(nttest, "id", 2) # Exception
object.__setattr__(nttest, "id", 2) # Exception
@dataclass(frozen=True)
class DCTest:
id: int
dctest = DCTest(1)
setattr(dctest, "id", 2) # Exception
object.__setattr__(dctest, "id", 2) # WORKS
namedtuple defines __slots__ = ()
因此您不能设置任何属性(它没有 __dict__
)。
Frozen dataclasses on the other hand perform a manual check in their __setattr__
方法并在它是冻结实例时引发异常。
比较以下:
>>> class Foo:
... __slots__ = ()
...
>>> f = Foo()
>>> f.__dict__ # doesn't exist, so object.__setattr__ won't work
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__dict__'
>>> @dataclass(frozen=True)
... class Bar:
... pass
...
>>> b = Bar()
>>> b.__dict__ # this exists, so object.__setattr__ works
{}
python Discord 频道上的其他人提供了另一个很好的见解:
冻结的数据class可能通过它自己的 setattr 限制访问,您可以通过将其传递给对象来绕过它。
命名元组派生自实际的元组而不是 python 中的实现,因此它们可以是“真正的”不可变的,但如果数据class(同样可以以更混乱的方式对元组进行)。
我建议使用 NamedTuple 来代替 namedtuple,因为它提供了适当的 class 的可读性,但如果您需要数据 class,那么冻结的数据应该没问题。 NamedTuple 并不是真正的类型提示,但在 class 定义中与它们一起使用,就像 dataclasses 所做的那样,并为它提供了比 type-like 构造函数更简洁的定义。
我认为没有一种方法可以限制纯 python 中的所有访问,因为您可以调用对象的有效方法。
在元组上赋值将涉及在 C 级别与 (c)python 交互,但在某种意义上是相同的,因为它不是它的预期接口。
Python 总的来说,这样的安全性并不大,如果不应该做某事,那么如果他们做了,那是用户的问题。
在我遇到数据类之前,我将命名元组用于不可变数据结构,我在用例中更喜欢它(与问题无关)。
现在我知道它们不是一成不变的!至少严格来说不是。
setattr(frozen_dc_obj, "prop", "value")
引发异常。好的
但为什么 object.__setattr__(frozen_dc_obj,..)
有效?
与namedtuple
相比,它引发了异常!
from collections import namedtuple
from dataclasses import dataclass
NTTest = namedtuple("NTTest", "id")
nttest = NTTest(1)
setattr(nttest, "id", 2) # Exception
object.__setattr__(nttest, "id", 2) # Exception
@dataclass(frozen=True)
class DCTest:
id: int
dctest = DCTest(1)
setattr(dctest, "id", 2) # Exception
object.__setattr__(dctest, "id", 2) # WORKS
namedtuple defines __slots__ = ()
因此您不能设置任何属性(它没有 __dict__
)。
Frozen dataclasses on the other hand perform a manual check in their __setattr__
方法并在它是冻结实例时引发异常。
比较以下:
>>> class Foo:
... __slots__ = ()
...
>>> f = Foo()
>>> f.__dict__ # doesn't exist, so object.__setattr__ won't work
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__dict__'
>>> @dataclass(frozen=True)
... class Bar:
... pass
...
>>> b = Bar()
>>> b.__dict__ # this exists, so object.__setattr__ works
{}
python Discord 频道上的其他人提供了另一个很好的见解:
冻结的数据class可能通过它自己的 setattr 限制访问,您可以通过将其传递给对象来绕过它。
命名元组派生自实际的元组而不是 python 中的实现,因此它们可以是“真正的”不可变的,但如果数据class(同样可以以更混乱的方式对元组进行)。 我建议使用 NamedTuple 来代替 namedtuple,因为它提供了适当的 class 的可读性,但如果您需要数据 class,那么冻结的数据应该没问题。 NamedTuple 并不是真正的类型提示,但在 class 定义中与它们一起使用,就像 dataclasses 所做的那样,并为它提供了比 type-like 构造函数更简洁的定义。
我认为没有一种方法可以限制纯 python 中的所有访问,因为您可以调用对象的有效方法。
在元组上赋值将涉及在 C 级别与 (c)python 交互,但在某种意义上是相同的,因为它不是它的预期接口。
Python 总的来说,这样的安全性并不大,如果不应该做某事,那么如果他们做了,那是用户的问题。