gh-103000: Optimise dataclasses asdict/astuple for common types (#103005)

Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
David Ellis 2023-04-10 22:50:58 +01:00 committed by GitHub
parent f80014a9b0
commit d034590294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 2 deletions

View File

@ -222,6 +222,29 @@ _POST_INIT_NAME = '__post_init__'
# https://bugs.python.org/issue33453 for details.
_MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)')
# Atomic immutable types which don't require any recursive handling and for which deepcopy
# returns the same object. We can provide a fast-path for these types in asdict and astuple.
_ATOMIC_TYPES = frozenset({
# Common JSON Serializable types
types.NoneType,
bool,
int,
float,
str,
# Other common types
complex,
bytes,
# Other types that are also unaffected by deepcopy
types.EllipsisType,
types.NotImplementedType,
types.CodeType,
types.BuiltinFunctionType,
types.FunctionType,
type,
range,
property,
})
# This function's logic is copied from "recursive_repr" function in
# reprlib module to avoid dependency.
def _recursive_repr(user_function):
@ -1291,7 +1314,9 @@ def asdict(obj, *, dict_factory=dict):
def _asdict_inner(obj, dict_factory):
if _is_dataclass_instance(obj):
if type(obj) in _ATOMIC_TYPES:
return obj
elif _is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _asdict_inner(getattr(obj, f.name), dict_factory)
@ -1363,7 +1388,9 @@ def astuple(obj, *, tuple_factory=tuple):
def _astuple_inner(obj, tuple_factory):
if _is_dataclass_instance(obj):
if type(obj) in _ATOMIC_TYPES:
return obj
elif _is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _astuple_inner(getattr(obj, f.name), tuple_factory)

View File

@ -0,0 +1,2 @@
Improve performance of :func:`dataclasses.astuple` and
:func:`dataclasses.asdict` in cases where the contents are common Python types.