I strongly agree with Ka-Ping. '+' is intuitively concatenation not merging. The behavior is overwhelmingly more similar to the '|' operator in sets (whether or not a user happens to know the historical implementation overlap). I think growing the full collection of set operations world be a pleasant addition to dicts. I think shoe-horning in plus would always be jarring to me. On Wed, Mar 6, 2019, 5:30 AM Ka-Ping Yee <zestyping@gmail.com> wrote:
len(dict1 + dict2) does not equal len(dict1) + len(dict2), so using the + operator is nonsense.
len(dict1 + dict2) cannot even be computed by any expression involving +. Using len() to test the semantics of the operation is not arbitrary; the fact that the sizes do not add is a defining quality of a merge. This is a merge, not an addition. The proper analogy is to sets, not lists.
The operators should be |, &, and -, exactly as for sets, and the behaviour defined with just three rules:
1. The keys of dict1 [op] dict2 are the elements of dict1.keys() [op] dict2.keys().
2. The values of dict2 take priority over the values of dict1.
3. When either operand is a set, it is treated as a dict whose values are None.
This yields many useful operations and, most importantly, is simple to explain. "sets and dicts can |, &, -" takes up less space in your brain than "sets can |, &, - but dicts can only + and -, where dict + is like set |".
merge and update some items:
{'a': 1, 'b': 2} | {'b': 3, 'c': 4} => {'a': 1, 'b': 3, 'c': 4}
pick some items:
{'a': 1, 'b': 2} & {'b': 3, 'c': 4} => {'b': 3}
remove some items:
{'a': 1, 'b': 2} - {'b': 3, 'c': 4} => {'a': 1}
reset values of some keys:
{'a': 1, 'b': 2} | {'b', 'c'} => {'a': 1, 'b': None, 'c': None}
ensure certain keys are present:
{'b', 'c'} | {'a': 1, 'b': 2} => {'a': 1, 'b': 2, 'c': None}
pick some items:
{'b', 'c'} | {'a': 1, 'b': 2} => {'b': 2}
remove some items:
{'a': 1, 'b': 2} - {'b', 'c'} => {'a': 1}
On Wed, Mar 6, 2019 at 1:51 AM Rémi Lapeyre <remi.lapeyre@henki.fr> wrote:
Le 6 mars 2019 à 10:26:15, Brice Parent (contact@brice.xyz(mailto:contact@brice.xyz)) a écrit:
Le 05/03/2019 à 23:40, Greg Ewing a écrit :
Steven D'Aprano wrote:
The question is, is [recursive merge] behaviour useful enough and common enough to be built into dict itself?
I think not. It seems like just one possible way of merging values out of many. I think it would be better to provide a merge function or method that lets you specify a function for merging values.
That's what this conversation led me to. I'm not against the addition for the most general usage (and current PEP's describes the behaviour I would expect before reading the doc), but for all other more specific usages, where we intend any special or not-so-common behaviour, I'd go with modifying Dict.update like this:
foo.update(bar, on_collision=updator) # Although I'm not a fan of the keyword I used
Le 6 mars 2019 à 10:26:15, Brice Parent (contact@brice.xyz(mailto:contact@brice.xyz)) a écrit:
Le 05/03/2019 à 23:40, Greg Ewing a écrit :
Steven D'Aprano wrote:
The question is, is [recursive merge] behaviour useful enough and common enough to be built into dict itself?
I think not. It seems like just one possible way of merging values out of many. I think it would be better to provide a merge function or method that lets you specify a function for merging values.
That's what this conversation led me to. I'm not against the addition for the most general usage (and current PEP's describes the behaviour I would expect before reading the doc), but for all other more specific usages, where we intend any special or not-so-common behaviour, I'd go with modifying Dict.update like this:
foo.update(bar, on_collision=updator) # Although I'm not a fan of the keyword I used
This won’t be possible update() already takes keyword arguments:
foo = {} bar = {'a': 1} foo.update(bar, on_collision=lambda e: e) foo {'a': 1, 'on_collision': <function <lambda> at 0x10b8df598>}
`updator` being a simple function like this one:
def updator(updated, updator, key) -> Any: if key == "related": return updated[key].update(updator[key])
if key == "tags": return updated[key] + updator[key]
if key in ["a", "b", "c"]: # Those return updated[key]
return updator[key]
There's nothing here that couldn't be made today by using a custom update function, but leaving the burden of checking for values that are in both and actually inserting the new values to Python's language, and keeping on our side only the parts that are specific to our use case, makes in my opinion the code more readable, with fewer possible bugs and possibly better optimization.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/