[Python-ideas] add an additional dataclasses.asdict option for non-dataclasses

Rhodri James rhodri at kynesim.co.uk
Thu May 9 07:26:18 EDT 2019


On 04/05/2019 21:58, Ricky Teachey wrote:
>> Are "asdict" APIs all that common?
> 
> There are at least two in the std lib: namedtuple._asdict(), and
> dataclasses.asdict. I assume there are others I'm not aware of.
> 
>> Conventionally you would get the caller to do this...
>> Anything else involves lots of guesswork...
> 
>> The problem with "casting" a generic non-dataclass object to a dict is
>> that it's not remotely obvious how you do that.  What key do you use if
>> your object is a simple int or string?  If you have even a
>> straightforward user class aren't just using "thing.__dict__", what
>> attributes do or do not make it into the dictionary?
> 
> I get the impression that what I am suggesting wasn't clear. Let me
> approach it from a different direction.
> 
> The language already provides the mapping protocol for user defined
> dict-making; ie, dict(myobj). So "this is how you define dict-making"
> rabbit hole is not only not worth going down, it's already been
> addressed for a very long time.

dict(myobj) is only defined for mapping objects.  For everything else, 
"dict-making" *isn't* addressed.

(Also, it's not a protocol.  I think what you want is a protocol, but 
you aren't specifying it well enough for me to grasp.)

> The problem I want to address is very specific and two pronged:
> 
> 1. some of the std lib modules DON'T USE the mapping protocol (for
> various very legitimate reasons)
> 
> 2. some user-defined types also don't use the mapping protocol, also
> often for legit reasons
> 
> Therefore the proposal is not to guess anything at all. The idea is to
> rigorously track all the existing dict APIs that have been put into
> the std lib over the years into a dict-making convenience function,
> since, for example, dict(my_dataclass) and dict(my_namedtuple) are
> (legitimately) broken.

Actually dict() would be the place to do this, surely?  If all you want 
is to add dataclasses and namedtuples to the list of things dict() copes 
with, that's entirely reasonable.  If you have a list of obviously 
mapping-like things dict() should cope with, that's fine.  If you want 
someone else to go do the work for you, that's less fine, but hey, 
someone might think it's an interesting thing to do.  If you want dict() 
to cope with arbitrary objects (which you still appear to), you need to 
say how.

> One can then confidently apply that asdict() convenience function
> without having to resort to a complicated string of if statements that
> might not even catch all cases.

I still think this is an unhelpful starting place.  I would actually be 
hesitant to use an API that just said "oh, throw anything at me, I'll 
cope."  I'd much rather use something that told me what format it 
expected data in, what I need to conform to, and do any conversion 
myself.  With half an ounce of luck I'll be able to arrange to get the 
data in the right format in the first place and avoid a whole 
unnecessary layer of processing.


> For user-defined protocols, we simply leave HOW to dict-ify to the
> caller via the factory_dict argument (if it is provided).

I am minded of the Larson cartoon, "...and now a miracle occurs..." 
This is the first time you've mentioned this argument (to dict(), 
presumably?).

> If it isn't
> provided, one of the following would happen:
> 
>   - raise an error -- similar or even identical to, perhaps, the error
> raised for dict("foo") or dict(42)
> 
>   - use the default dict_factory (which is dict; identical to above,
> but succeeds if there is a dict-casting protocol already implemented)
> 
> I favor the second, but I'm not dogmatic about which of those would be better.
> 
> Again: no interest in trying to guess at user defined dict-making APIs
> as such, aside from allowing the caller to provide that API themselves
> if they so wish.

Right.  So what you want is to add a keyword argument to dict() such that
     dict(obj, factory_dict=dict_me_harder)

calls dict_me_harder(obj) if obj is not a type dict() knows how to deal 
with natively, expecting it to return a new dict or raise an exception. 
Is that right?

-- 
Rhodri James *-* Kynesim Ltd


More information about the Python-ideas mailing list