This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eric.smith
Recipients NeilGirdhar, alexdelorenzo, eric.smith, levkivskyi, rhettinger
Date 2018-09-11.18:36:03
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1536690963.87.0.0269046726804.issue34363@psf.upfronthosting.co.za>
In-reply-to
Content
The question here is: what should asdict() return if the dataclass contains a namedtuple? What the code is trying to do (but currently failing!) is to return another namedtuple, but with the values returned by recursively calling in to asdict() (or rather, its helper function) for each field in the namedtuple.

I think this is the correct behavior. Specifically, I do not want to call the namedtuple's _asdict() method. There are two problems with _asdict():

1. It doesn't recurse in to the fields, like asdict() normally does with a dict, list, or tuple.

2. It returns a dict! This is a problem because if a dataclass field contains a dict which has a key which is a namedtuple, then asdict() would fail.

Here's an example of #2 above, if asdict() on a namedtuple field returns a dict:

@dataclass
class C:
    f: 'Any'

T = namedtuple('T', 'a')

c = C({T('an a'): 0})
print('c:', c)
print(asdict(c))   # <- error here

prints:

c: C(f={T(a='an a'): 0})
Traceback (most recent call last):
...
  File "/home/eric/python/lib/dataclasses.py", line 1019, in asdict
    return _asdict_inner(obj, dict_factory)
  File "/home/eric/python/lib/dataclasses.py", line 1026, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
  File "/home/eric/python/lib/dataclasses.py", line 1059, in _asdict_inner
    for k, v in obj.items())
TypeError: unhashable type: 'collections.OrderedDict'

So, although it's unfortunate, I think the only reasonable thing to do in this case is to have asdict() on a namedtuple return another namedtuple. Here's how that looks using the above code:

c: C(f={T(a='an a'): 0})
{'f': {T(a='an a'): 0}}

Admittedly, this can't be used with json.dumps() (you get "TypeError: keys must be str, int, float, bool or None, not T"), but I think it's the best we can do. It's consistent with any other class derived from tuple or list: asdict() will convert it to a copy of the class, recursing into each item that's in the tuple or list.
History
Date User Action Args
2018-09-11 18:36:03eric.smithsetrecipients: + eric.smith, rhettinger, NeilGirdhar, levkivskyi, alexdelorenzo
2018-09-11 18:36:03eric.smithsetmessageid: <1536690963.87.0.0269046726804.issue34363@psf.upfronthosting.co.za>
2018-09-11 18:36:03eric.smithlinkissue34363 messages
2018-09-11 18:36:03eric.smithcreate