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.
As you had perceived well, one has to go well out of his way to modify one of these attributtes.
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 writting 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 teh 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 circunvent 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: