From 4ce03441a87addb5bfdbadb8e16cc5b573293736 Mon Sep 17 00:00:00 2001 From: Io Mintz Date: Wed, 2 Oct 2019 20:34:12 +0000 Subject: [PATCH] dataclasses: generate the _hash_action table more cleanly currently it's a table, which is more efficient than an if-else chain, but less efficient. We can have the best of both worlds by converting https://bugs.python.org/issue32929#msg312829 into a loop that generates the table that we currently have. --- Lib/dataclasses.py | 62 +++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 9135b07c9f259a..949ff74347caa6 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -6,6 +6,7 @@ import keyword import builtins import functools +import itertools import _thread @@ -762,34 +763,39 @@ def _hash_exception(cls, fields): raise TypeError(f'Cannot overwrite attribute __hash__ ' f'in class {cls.__name__}') -# -# +-------------------------------------- unsafe_hash? -# | +------------------------------- eq? -# | | +------------------------ frozen? -# | | | +---------------- has-explicit-hash? -# | | | | -# | | | | +------- action -# | | | | | -# v v v v v -_hash_action = {(False, False, False, False): None, - (False, False, False, True ): None, - (False, False, True, False): None, - (False, False, True, True ): None, - (False, True, False, False): _hash_set_none, - (False, True, False, True ): None, - (False, True, True, False): _hash_add, - (False, True, True, True ): None, - (True, False, False, False): _hash_add, - (True, False, False, True ): _hash_exception, - (True, False, True, False): _hash_add, - (True, False, True, True ): _hash_exception, - (True, True, False, False): _hash_add, - (True, True, False, True ): _hash_exception, - (True, True, True, False): _hash_add, - (True, True, True, True ): _hash_exception, - } -# See https://bugs.python.org/issue32929#msg312829 for an if-statement -# version of this table. +def _gen_hash_action(): + tab = {} + for state in itertools.product((False, True), repeat=4): + unsafe_hash, has_explicit_hash, eq, frozen = state + + if unsafe_hash: + # If there's already a __hash__, raise TypeError, otherwise add __hash__. + if has_explicit_hash: + hash_action = _hash_exception + else: + hash_action = _hash_add + else: + # unsafe_hash is False (the default). + if has_explicit_hash: + # There's already a __hash__, don't overwrite it. + hash_action = None + else: + if eq and frozen: + # It's frozen and we added __eq__, generate __hash__. + hash_action = _hash_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 = _hash_set_none + else: + # There's no __eq__, use the base class __hash__. + hash_action = None + + tab[state] = hash_action + + return tab + +_hash_action = _gen_hash_action() def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):