-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change dataclasses hashing to use unsafe_hash boolean (default to False) #77110
Comments
See https://mail.python.org/pipermail/python-dev/2018-February/152150.html for a discussion. This will remove the @DataClass hash= tri-state parameter, and replace it with an unsafe_hash= boolean-only parameter. From that thread: """
If we instead use @DataClass(unsafe_hash=True), a __hash__ will be added Other values (e.g. unsafe_hash=None) are illegal for this flag. |
This is a truly awful name, there's nothing unsafe about the hash |
That's a quote from Guido. There weren't any better ideas on the python-dev thread. |
It is important to convey the idea that if you are thinking of using this you are probably doing something fishy. An alternative could be |
Note that this class (from the test suite) will now raise an exception: @dataclass(unsafe_hash=True)
class C:
i: int
def __eq__(self, other):
return self.i == other.i That's because it has a __hash__, added when __eq__ is defined. I think we're better off with the rule being: If unsafe_hash=True, raise an exception if __hash__ exists and is not None. Otherwise add __hash__. That's how it used to be with hash=True (in 3.70b1). Or is that what you meant by "but if a __hash__ method is present, an exception is raised"? Notice the word "method", instead of attribute. |
Sorry, I used imprecise language. What you propose is fine. On Feb 24, 2018 09:54, "Eric V. Smith" <report@bugs.python.org> wrote:
|
Here's the simplest way I can describe this, and it's also the table used inside the code. The column "has-explicit-hash?" is trying to answer the question "is there a __hash__ function defined in this class?". It is set to False if either __hash__ is missing, or if __hash__ is None and there's an __eq__ function. I'm assuming that the __hash__ is implicit in the latter case. # Decide if/how we're going to create a hash function. Key is
# (unsafe_hash, eq, frozen, does-hash-exist). Value is the action to
# take.
# Actions:
# '': Do nothing.
# 'none': Set __hash__ to None.
# 'add': Always add a generated __hash__function.
# 'exception': Raise an exception.
#
# +-------------------------------------- unsafe_hash?
# | +------------------------------- eq?
# | | +------------------------ frozen?
# | | | +---------------- has-explicit-hash?
# | | | |
# | | | | +------- action
# | | | | |
# v v v v v
_hash_action = {(False, False, False, False): (''),
(False, False, False, True ): (''),
(False, False, True, False): (''),
(False, False, True, True ): (''),
(False, True, False, False): ('none'),
(False, True, False, True ): (''),
(False, True, True, False): ('add'),
(False, True, True, True ): (''),
(True, False, False, False): ('add'),
(True, False, False, True ): ('exception'),
(True, False, True, False): ('add'),
(True, False, True, True ): ('exception'),
(True, True, False, False): ('add'),
(True, True, False, True ): ('exception'),
(True, True, True, False): ('add'),
(True, True, True, True ): ('exception'),
} PR will be ready as soon as I clean a few things up. |
I don't know if I'm unique, but I find such a table with 4 Boolean keys |
if unsafe_hash:
# If there's already a __hash__, raise TypeError, otherwise add __hash__.
if has_explicit_hash:
hash_action = 'exception'
else:
hash_action = 'add'
else:
# unsafe_hash is False (the default).
if has_explicit_hash:
# There's already a __hash__, don't overwrite it.
hash_action = ''
else:
if eq and frozen:
# It's frozen and we added __eq__, generate __hash__.
hash_action = 'add'
elif eq and not frozen:
# It's not frozen but has __eq__, make it unhashable.
# This is the default if no params to @dataclass.
hash_action = 'none'
else:
# There's no __eq__, use the base class __hash__.
hash_action = '' |
I've been focused on getting the code fix in for the next beta release on 2018-02-26, and leaving the possible parameter name change for later. I don't feel strongly about the parameter name. Right now it's unsafe_hash, per Guido's original proposal. |
@steven.daprano the name was +1'ed by many people in the python-dev discussion as sending the right message. So I'm reluctant to change it. If you can cause a landslide of support for |
I'm going to close this. Steven: if you gain support for a parameter name change, please open another issue. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: