summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorDavid Ellis <ducksual@gmail.com>2023-04-10 21:50:58 (GMT)
committerGitHub <noreply@github.com>2023-04-10 21:50:58 (GMT)
commitd034590294d4618880375a6db513c30bce3e126b (patch)
treeb8d89889a1e1de87837ca9739384872b9d7444bd /Lib
parentf80014a9b0e8d00df3e65379070be1dfd2721682 (diff)
downloadcpython-d034590294d4618880375a6db513c30bce3e126b.zip
cpython-d034590294d4618880375a6db513c30bce3e126b.tar.gz
cpython-d034590294d4618880375a6db513c30bce3e126b.tar.bz2
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>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/dataclasses.py31
1 files changed, 29 insertions, 2 deletions
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 7558287..4026c8b 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -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)