classification
Title: Make fields selectively immutable in dataclasses
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: eric.smith Nosy List: eric.smith, remi.lapeyre, rhettinger, yselivanov
Priority: normal Keywords:

Created on 2018-12-18 21:13 by rhettinger, last changed 2019-01-02 00:12 by remi.lapeyre.

Messages (3)
msg332080 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2018-12-18 21:13
The unsafe_hash option is unsafe only because it doesn't afford mutability protections.  This can be mitigated with selective immutability.

@dataclass
class Person:
    ssn: int = field(immutable=True)
    birth_city: int = field(immutable=True)
    name: str      # A person can change their name
    address: str   # A person can move  
    age: int       # An age can change

This would generate something like this:

   def __setattr__(self, attr, value):
      if attr in {'ssn', 'birth_city'} and hasattr(self, attr):
           raise TypeError(
               f'{attr!r} is not settable after initialization')
      return object.__setattr__(self, name, attr)

A number of APIs are possible -- the important thing to be able to selectively block updates to particular fields (particularly those used in hashing and ordering).
msg332086 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-12-18 21:53
+1.  I've tried to use `field(frozen=True)` today and was surprised it's not supported.
msg332847 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2019-01-02 00:12
Hi @rhettinger, this is similar to #33474.

I started working on an implementation of this.

With the implementation you propose, if a field has both init=True and frozen=True, it may be set after the creation of the instance:

        @dataclass
        class Person:
            ssn: int = field(init=False, frozen=True)
            name: str

        p = Person(name='foo')
        p.name = 'bar'
        p.ssn = 1234

Wouldn't this conflict with the purpose of safe hashing?

I think it would need __setattr__ to be:

      def __setattr__(self, attr, value):
            if attr in {'ssn', 'birth_city'}:
                 raise TypeError(
                     f'{attr!r} is not settable after initialization')
            return super(cls, self).__setattr__(self, name, attr)

wouldn't it?
History
Date User Action Args
2019-01-02 00:12:54remi.lapeyresetnosy: + remi.lapeyre
messages: + msg332847
2018-12-18 21:53:28yselivanovsetnosy: + yselivanov
messages: + msg332086
2018-12-18 21:13:53rhettingercreate