[Python-Dev] Dataclasses and correct hashability
Guido van Rossum
guido at python.org
Mon Feb 5 12:46:05 EST 2018
I'm sorry, but a solution that requires __class__ assignment is way too
fragile for my taste.
On Mon, Feb 5, 2018 at 6:28 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> On 5 February 2018 at 15:49, Guido van Rossum <guido at python.org> wrote:
> > My point is that once you have one of those patterns in place, changing
> your
> > code to avoid them may be difficult. And yet your code may treat the
> objects
> > as essentially immutable after the initialization phase (e.g. a parse
> tree).
> > So if you create a dataclass and start coding like that for a while, and
> > much later you need to put one of these into a set or use it as a dict
> key,
> > switching to frozen=True may not be a quick option. And writing a
> __hash__
> > method by hand may feel like a lot of busywork. So this is where
> > [unsafe_]hash=True would come in handy.
> >
> > I think naming the flag unsafe_hash should take away most objections,
> since
> > it will be clear that this is not a safe thing to do. People who don't
> > understand the danger are likely to copy a worse solution from
> StackOverflow
> > anyway. The docs can point to frozen=True and explain the danger.
>
> Aye, calling the flag unsafe_hash would convert me from -1 to -0.
>
> The remaining -0 is because I think there's a different and more
> robust way to tackle your example use case:
>
> # Mutable initialization phase
> >>> from dataclasses import dataclass
> >>> @dataclass
> ... class Example:
> ... a: int
> ... b: int
> ...
> >>> c = Example(None, None)
> >>> c
> Example(a=None, b=None)
> >>> c.a = 1
> >>> c.b = 2
> >>> c
> Example(a=1, b=2)
>
>
> # Frozen usage phase
> >>> @dataclass(frozen=True)
> ... class LockedExample(Example):
> ... pass
> ...
> >>> c.__class__ = LockedExample
> >>> c.a = 1
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "/home/ncoghlan/devel/cpython/Lib/dataclasses.py", line 448,
> in _frozen_setattr
> raise FrozenInstanceError(f'cannot assign to field {name!r}')
> dataclasses.FrozenInstanceError: cannot assign to field 'a'
> >>> c.b = 2
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "/home/ncoghlan/devel/cpython/Lib/dataclasses.py", line 448,
> in _frozen_setattr
> raise FrozenInstanceError(f'cannot assign to field {name!r}')
> dataclasses.FrozenInstanceError: cannot assign to field 'b'
> >>> hash(c)
> 3713081631934410656
>
> The gist of that approach is to assume that there will be *somewhere*
> in the code where it's possible to declare the construction of the
> instance "complete", and flip the nominal class over to the frozen
> subclass to make further mutation unlikely, even though the true
> underlying type is still the mutable version.
>
> That said, if we do provide "unsafe_hash", then the documentation for
> that flag becomes a place where we can explicitly suggest using a
> frozen subclass instead.
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
>
--
--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20180205/b4050320/attachment.html>
More information about the Python-Dev
mailing list