Skip to content

Commit f96ddad

Browse files
authored
Trivial dataclass cleanups: (GH-6218)
- When adding a single element to a list, use .append() instead of += and creating a new list. - For consistency, import the copy module, instead of just deepcopy. This leaves only a module at the class level, instead of a function. - Improve some comments. - Improve some whitespace. - Use tuples instead of lists. - Simplify a test.
1 parent a95d986 commit f96ddad

File tree

1 file changed

+26
-23
lines changed

1 file changed

+26
-23
lines changed

Lib/dataclasses.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
2+
import copy
23
import types
3-
from copy import deepcopy
44
import inspect
55

66
__all__ = ['dataclass',
@@ -137,7 +137,7 @@
137137
# For boxes that are blank, __hash__ is untouched and therefore
138138
# inherited from the base class. If the base is object, then
139139
# id-based hashing is used.
140-
# Note that a class may have already __hash__=None if it specified an
140+
# Note that a class may already have __hash__=None if it specified an
141141
# __eq__ method in the class body (not one that was created by
142142
# @dataclass).
143143
# See _hash_action (below) for a coded version of this table.
@@ -147,7 +147,7 @@
147147
class FrozenInstanceError(AttributeError): pass
148148

149149
# A sentinel object for default values to signal that a
150-
# default-factory will be used.
150+
# default factory will be used.
151151
# This is given a nice repr() which will appear in the function
152152
# signature of dataclasses' constructors.
153153
class _HAS_DEFAULT_FACTORY_CLASS:
@@ -249,6 +249,7 @@ class _DataclassParams:
249249
'unsafe_hash',
250250
'frozen',
251251
)
252+
252253
def __init__(self, init, repr, eq, order, unsafe_hash, frozen):
253254
self.init = init
254255
self.repr = repr
@@ -267,6 +268,7 @@ def __repr__(self):
267268
f'frozen={self.frozen}'
268269
')')
269270

271+
270272
# This function is used instead of exposing Field creation directly,
271273
# so that a type checker can be told (via overloads) that this is a
272274
# function whose type depends on its parameters.
@@ -307,6 +309,8 @@ def _tuple_str(obj_name, fields):
307309
def _create_fn(name, args, body, *, globals=None, locals=None,
308310
return_type=MISSING):
309311
# Note that we mutate locals when exec() is called. Caller beware!
312+
# The only callers are internal to this module, so no worries
313+
# about external callers.
310314
if locals is None:
311315
locals = {}
312316
return_annotation = ''
@@ -429,26 +433,25 @@ def _init_fn(fields, frozen, has_post_init, self_name):
429433

430434
body_lines = []
431435
for f in fields:
432-
# Do not initialize the pseudo-fields, only the real ones.
433436
line = _field_init(f, frozen, globals, self_name)
434-
if line is not None:
435-
# line is None means that this field doesn't require
436-
# initialization. Just skip it.
437+
# line is None means that this field doesn't require
438+
# initialization (it's a pseudo-field). Just skip it.
439+
if line:
437440
body_lines.append(line)
438441

439442
# Does this class have a post-init function?
440443
if has_post_init:
441444
params_str = ','.join(f.name for f in fields
442445
if f._field_type is _FIELD_INITVAR)
443-
body_lines += [f'{self_name}.{_POST_INIT_NAME}({params_str})']
446+
body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str})')
444447

445448
# If no body lines, use 'pass'.
446449
if not body_lines:
447450
body_lines = ['pass']
448451

449452
locals = {f'_type_{f.name}': f.type for f in fields}
450453
return _create_fn('__init__',
451-
[self_name] +[_init_param(f) for f in fields if f.init],
454+
[self_name] + [_init_param(f) for f in fields if f.init],
452455
body_lines,
453456
locals=locals,
454457
globals=globals,
@@ -457,7 +460,7 @@ def _init_fn(fields, frozen, has_post_init, self_name):
457460

458461
def _repr_fn(fields):
459462
return _create_fn('__repr__',
460-
['self'],
463+
('self',),
461464
['return self.__class__.__qualname__ + f"(' +
462465
', '.join([f"{f.name}={{self.{f.name}!r}}"
463466
for f in fields]) +
@@ -496,7 +499,7 @@ def _cmp_fn(name, op, self_tuple, other_tuple):
496499
# '(other.x,other.y)'.
497500

498501
return _create_fn(name,
499-
['self', 'other'],
502+
('self', 'other'),
500503
[ 'if other.__class__ is self.__class__:',
501504
f' return {self_tuple}{op}{other_tuple}',
502505
'return NotImplemented'])
@@ -505,12 +508,12 @@ def _cmp_fn(name, op, self_tuple, other_tuple):
505508
def _hash_fn(fields):
506509
self_tuple = _tuple_str('self', fields)
507510
return _create_fn('__hash__',
508-
['self'],
511+
('self',),
509512
[f'return hash({self_tuple})'])
510513

511514

512515
def _get_field(cls, a_name, a_type):
513-
# Return a Field object, for this field name and type. ClassVars
516+
# Return a Field object for this field name and type. ClassVars
514517
# and InitVars are also returned, but marked as such (see
515518
# f._field_type).
516519

@@ -560,9 +563,9 @@ def _get_field(cls, a_name, a_type):
560563
raise TypeError(f'field {f.name} cannot have a '
561564
'default factory')
562565
# Should I check for other field settings? default_factory
563-
# seems the most serious to check for. Maybe add others. For
564-
# example, how about init=False (or really,
565-
# init=<not-the-default-init-value>)? It makes no sense for
566+
# seems the most serious to check for. Maybe add others.
567+
# For example, how about init=False (or really,
568+
# init=<not-the-default-init-value>)? It makes no sense for
566569
# ClassVar and InitVar to specify init=<anything>.
567570

568571
# For real fields, disallow mutable defaults for known types.
@@ -903,7 +906,7 @@ def _asdict_inner(obj, dict_factory):
903906
return type(obj)((_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory))
904907
for k, v in obj.items())
905908
else:
906-
return deepcopy(obj)
909+
return copy.deepcopy(obj)
907910

908911

909912
def astuple(obj, *, tuple_factory=tuple):
@@ -943,7 +946,7 @@ def _astuple_inner(obj, tuple_factory):
943946
return type(obj)((_astuple_inner(k, tuple_factory), _astuple_inner(v, tuple_factory))
944947
for k, v in obj.items())
945948
else:
946-
return deepcopy(obj)
949+
return copy.deepcopy(obj)
947950

948951

949952
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
@@ -1032,9 +1035,9 @@ class C:
10321035
if f.name not in changes:
10331036
changes[f.name] = getattr(obj, f.name)
10341037

1035-
# Create the new object, which calls __init__() and __post_init__
1036-
# (if defined), using all of the init fields we've added and/or
1037-
# left in 'changes'.
1038-
# If there are values supplied in changes that aren't fields, this
1039-
# will correctly raise a TypeError.
1038+
# Create the new object, which calls __init__() and
1039+
# __post_init__() (if defined), using all of the init fields
1040+
# we've added and/or left in 'changes'. If there are values
1041+
# supplied in changes that aren't fields, this will correctly
1042+
# raise a TypeError.
10401043
return obj.__class__(**changes)

0 commit comments

Comments
 (0)