Python修改冷冻数据类
我有一个Python Dataclass,其属性我不打算修改。 但是,正如我刚刚意识到的那样,它仍然可以通过__ dict __
甚至进行修改。例如:
@dataclass(frozen=True)
class C:
a: int = 1
c = C()
c.a = 2 # dataclasses.FrozenInstanceError: cannot assign to field 'a'
c.__dict__['a'] = 2 # legit
print(c)
# C(a=2)
我知道这是基础__ dict __
attr at at __ _______etattr __
并没有真正覆盖的基础__ dict __ dict __
attr。
另一方面,我确实需要以集体方式访问所有属性。这种任务的最佳实践是什么?我是否制作__ dict __
的副本?也许插槽= true
可能是潜在的候选人?
提前致谢。
I have a python dataclass whose attributes I didn't intend to modify.
However, as I just realized, it is still modifiable via __dict__
even when I set frozen=True
. For instance:
@dataclass(frozen=True)
class C:
a: int = 1
c = C()
c.a = 2 # dataclasses.FrozenInstanceError: cannot assign to field 'a'
c.__dict__['a'] = 2 # legit
print(c)
# C(a=2)
I understand that this is owning to the underlying __dict__
attr is a mutable object, and raising error at __setattr__
doesn't really cover.
On the other hand, I do have a need to access all the attributes in a collective manner. What is the best practice for this kind of task? Do I make a copy of __dict__
? Or maybe slots=True
would be a potential candidate?
thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您所感知到的那样,必须避开 来修改这些属性之一。
虽然可以使访问实例的
__ dict __
直接访问属性更加困难,但人们将可以在python中更改基础属性。 ,由于语言的动态和反思性。确实不是“想要”“想要”更改属性修改它的语言的目的(请注意,在语言中,“私有”和“受保护”属性具有C ++和Java,Java和Java可以也。换句话说,在A 严重系统中没有场景可以修改属性,通过一个可以访问系统代码或类的属性,应该是“危险的”,而对于开发人员来说,编写将在该代码中运行的代码的限制相同的过程。如果您有一些设计思考,那么您最好重新考虑,更有可能将“仅读取”数据放入单独的服务中,可以通过API或其他RPC方法访问,因此潜在的“恶意编码器”没有在过程中访问您的变量。
话虽如此,有一些方法可以冻结属性可能会阻止
__ dict __
方法 - 其中之一是创建一个特殊的描述符,在实例上进行一定的更改后会锁定写作:分配的属性对于班上的描述符,不会浏览__ dict __
。但是如上所述,任何打算更改属性的任何人都可以转到描述符的任何存储器都保留属性(必须位于某个位置),或者简单地逆转指示该值的更改从某个点上读取。我玩耍了,并提出了这样的描述符的示例代码,但这确实非常非常愚蠢 - 因为在某个时候,它必须使用“绕开自身的方式”以将类标记为锁定。但是,如您所描述的那样,使用
__插槽__
将与冷冻数据一起使用,并且不会具有可变的__ dict __ dict __
- 另一方面,通过进行更改实例属性仍然很容易通过类属性的.__设置__
方法:As you had perceived well, one has to go well out of his way to modify one of these attributes.
While it is possible to make it a bit more difficult than accessing the instance's
__dict__
directly to modify an attribute, it will always be possible for one to change the underlying attribute in Python, due to the dynamic and reflexive nature of the language. It is really not the intent of the language to prevent one that "wants" to change an attribute from modifying it (note that 'private' and 'protected' attributes in languages that feature them like C++ and Java can also be altered by one intending to do so by using reflexive API's or going through the name mangling).In other words there is no scenario in a serious system that modifying an attribute, by one having access to the system code or classes should be "dangerous" and off limits for developers writing code that will run in the same process. If you have some designing thinking so, you'd better rethink that, more likely putting the "read only" data in a separate service, accessible via API or other RPC method, so that potential "malicious coders" do not have in process access to your variable.
All that said, there are ways to freeze attributes that might prevent the
__dict__
approach - one of which would be to create a special descriptor that after a certain change on the instance would lock writing: attributes that are assigned to a descriptor in the class won't go through__dict__
. But as stated above, anyone intending to change the attribute can go to whichever storage the descriptor keeps the attribute in (it has to be in a certain place), or simply reverse the change that indicates the value should be read-only from a certain point on.I played around, and came up with example code for such a descriptor, but it is really, really silly - as it at some point has to use the "way to circumvent itself" in order to mark the class as locked. However, using
__slots__
as you described will work with a frozen dataclass and will not feature a mutable__dict__
- on the other hand, it still trivial to change the instance attribute by going through the class attribute's.__set__
method: