@@ -207,6 +207,8 @@ def __repr__(self):
207207# The name of an attribute on the class where we store the Field
208208# objects. Also used to check if a class is a Data Class.
209209_FIELDS = '__dataclass_fields__'
210+ # The name of an attribute on the class where we store the field names
211+ _FIELD_NAMES = '__dataclass_field_names__'
210212
211213# The name of an attribute on the class that stores the parameters to
212214# @dataclass.
@@ -1052,6 +1054,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
10521054 # Remember all of the fields on our class (including bases). This
10531055 # also marks this class as being a dataclass.
10541056 setattr (cls , _FIELDS , fields )
1057+ setattr (cls , _FIELD_NAMES , tuple (f .name for f in fields .values () if f ._field_type is _FIELD ))
10551058
10561059 # Was this class defined with an explicit __hash__? Note that if
10571060 # __eq__ is defined in this class, then python will automatically
@@ -1196,13 +1199,13 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
11961199# the code instead of iterating over fields. But that can be a project for
11971200# another day, if performance becomes an issue.
11981201def _dataclass_getstate (self ):
1199- return [getattr (self , f . name ) for f in fields (self )]
1202+ return [getattr (self , name ) for name in _field_names (self )]
12001203
12011204
12021205def _dataclass_setstate (self , state ):
1203- for field , value in zip (fields (self ), state ):
1206+ for field_name , value in zip (_field_names (self ), state ):
12041207 # use setattr because dataclass may be frozen
1205- object .__setattr__ (self , field . name , value )
1208+ object .__setattr__ (self , field_name , value )
12061209
12071210
12081211def _get_slots (cls ):
@@ -1285,7 +1288,7 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
12851288
12861289 # Create a new dict for our new class.
12871290 cls_dict = dict (cls .__dict__ )
1288- field_names = tuple ( f . name for f in fields ( cls ) )
1291+ field_names = _field_names ( cls )
12891292 # Make sure slots don't overlap with those in base classes.
12901293 inherited_slots = set (
12911294 itertools .chain .from_iterable (map (_get_slots , cls .__mro__ [1 :- 1 ]))
@@ -1377,8 +1380,6 @@ def fields(class_or_instance):
13771380 Accepts a dataclass or an instance of one. Tuple elements are of
13781381 type Field.
13791382 """
1380-
1381- # Might it be worth caching this, per class?
13821383 try :
13831384 fields = getattr (class_or_instance , _FIELDS )
13841385 except AttributeError :
@@ -1388,6 +1389,13 @@ def fields(class_or_instance):
13881389 # order, so the order of the tuple is as the fields were defined.
13891390 return tuple (f for f in fields .values () if f ._field_type is _FIELD )
13901391
1392+ def _field_names (class_or_instance ):
1393+ """Return a tuple describing the field names of this dataclass.
1394+
1395+ Accepts a dataclass or an instance of one. Excludes pseudo-fields
1396+ """
1397+
1398+ return getattr (class_or_instance , _FIELD_NAMES )
13911399
13921400def _is_dataclass_instance (obj ):
13931401 """Returns True if obj is an instance of a dataclass."""
@@ -1433,13 +1441,13 @@ def _asdict_inner(obj, dict_factory):
14331441 # dataclass instance: fast path for the common case
14341442 if dict_factory is dict :
14351443 return {
1436- f . name : _asdict_inner (getattr (obj , f . name ), dict )
1437- for f in fields ( obj )
1444+ name : _asdict_inner (getattr (obj , name ), dict )
1445+ for name in _field_names ( obj_type )
14381446 }
14391447 else :
14401448 return dict_factory ([
1441- (f . name , _asdict_inner (getattr (obj , f . name ), dict_factory ))
1442- for f in fields ( obj )
1449+ (name , _asdict_inner (getattr (obj , name ), dict_factory ))
1450+ for name in _field_names ( obj_type )
14431451 ])
14441452 # handle the builtin types first for speed; subclasses handled below
14451453 elif obj_type is list :
@@ -1522,8 +1530,8 @@ def _astuple_inner(obj, tuple_factory):
15221530 return obj
15231531 elif _is_dataclass_instance (obj ):
15241532 return tuple_factory ([
1525- _astuple_inner (getattr (obj , f . name ), tuple_factory )
1526- for f in fields (obj )
1533+ _astuple_inner (getattr (obj , name ), tuple_factory )
1534+ for name in _field_names (obj )
15271535 ])
15281536 elif isinstance (obj , tuple ) and hasattr (obj , '_fields' ):
15291537 # obj is a namedtuple. Recurse into it, but the returned
0 commit comments