Summary: dict.update(self, other) corresponds to 'merge-right'. Perhaps add dict.gapfill(self, other), which corresponds to 'merge-left'. This post defines dict.gapfill, and discusses update and gapfill in the context of PEP 584.
PEP 584 suggests adding a merge operator (to be denoted by '|' or '+') to dictionaries, and a corresponding augmented assignment operator (to be denoted by '|=' or '+='). The main purpose of this post is to remind ourselves that there are both merge-left and merge-right operators, and to begin to discuss the consequences. Perhaps Off Topic: There is already a road sign for 'merge left'. See the following URL for a discussion of usual 'W4-2' sign for this operation, and how it can cause confusion. And how a "small but critical" change to the sign reduced confusion, in a backwards compatible way. https://99percentinvisible.org/article/lane-ends-merge-left-redesigning-w4-2-road-sign-end-confusion/ By the way, that URL writes: "The solution is elegant, simple and additive — it requires no fundamental reformatting, building instead on the existing sign. It is thus also recognizable to those familiar with its predecessor and visually similar to older signs that can still be found on the road." To me, it seems that the author has goals similar to the Python design goals. See also http://www.trafficsign.us/pdf/warn/w4-2mod.pdf, which uses 'Left Lane Ends' instead of 'Merge Right' (and similarly 'Right Lane Ends' for 'Merge Left'). Now Back On Topic. The dict.update(self, other) method gives priority to other. (In other words, it's 'merge-right'.) In March this year, it led me to realise that perhaps dict would benefit from there being a 'merge-left' operator, which gives priority to the left. Here's an example of how merge-left (which I call dict.gapfill) could be coded. (I've done it in a way that emphasises similarities and differences between dict.update and dict.gapfill.) class mydict(dict): def update(self, other): for key, value in other.items(): if True: self[key] = value def gapfill(self, other): for key, value in other.items(): if key not in self: self.key = value def copy(self): return mydict(super().copy()) Here's some simple examples of the semantics. >>> d1 = mydict(); d1[1] = 'int one' >>> d2 = mydict(); d2[1.0] = 'float one' >>> c1 = d1.copy(); c1.update(d2) >>> c2 = d1.copy(); c2.gapfill(d2) >>> c1 {1: 'float one'} >>> c2 {1: 'int one'} For comparison with sets (similar to dict keys), we also have >>> {1} | {1.0} {1} >>> {1.0} | {1} {1.0} Now for some consequences. First, it's my opinion that dict.gapfill(self, other) will be useful when we're given say some command line values, and we wish to augment this with default values. Second, it's my opinion that dict.gapfill(self, other) is in some cases a useful alternative for a dict merge operator (whether it uses '|' or '+' as its symbol). For evidence see https://github.com/jpadilla/pyjwt/blob/master/jwt/utils.py#L71-L81 Third, for sets the '|' operator is a merge-left operator. In other words it gives priority to the set-element (or key) in the first set, rather than the second. (This happens only when the two keys so-to-say are the same value represented by different types. In our case the types are (int, float).) Fourth, as the set '|' operator is merge-left, and Python's >>> value = A or B or C gives priority to the first (left-most) true value, confusion might result if the dict '|' operator were to be merge-right. Regarding the fourth point, it is possible that purity and consistency points to the dict '|' operator being merge-left, while usefulness and habit points to the dict '|' operator being merge-right. Fifth and finally. In our context, it is my opinion that merge-left and merge-right don't clearly communicate the differing semantics. However, in my opinion dict.update(self, other) and dict.gapfill(self, other) do clearly communicate their semantics. Here's a link to my post to this list in March this year. https://mail.python.org/archives/list/python-ideas@python.org/message/GRFYX3KG7BNUU7CMLZTRTH32MMI2V4TR/ with best regards Jonathan
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/BA34PUDUJR23HI7XUCCPAFI3TPTXH7W5/ Code of Conduct: http://python.org/psf/codeofconduct/