[Python-ideas] Allow using ** twice

Steven D'Aprano steve at pearwood.info
Fri Jun 7 09:31:38 CEST 2013


On 07/06/13 12:27, Nick Coghlan wrote:

> In an attempt to frame the discussion more productively:
>
> 1. Python already has a well-defined notion of what it means to merge
> two dictionaries: d1.update(d2)

With the proviso that while it might be Python's idea of merging two dicts, it isn't necessarily the most useful way of merging two dicts. I've sometimes found myself needing to ignore dict.update and write my own.

This is why I'm only luke-warm, at best, for any of these proposals. I expect that whatever solution gets taken up, if any, I'm still going to need to write my own helper function from time to time :-)


> 2. This notion is asymmetric, thus it makes sense to use an asymmetric
> notation for it (specifically, a method call) rather than a
> traditionally symmetric binary operator notation like + or |.

The usual term for this is "commutative". But note that many non-commutative operations are still written with binary operators. We don't get confused by the fact that:

7-2 != 2-7

1/5 != 5/1

3**4 != 4**3

or even:

"foo" + "bar" != "bar" + "foo"


so while the non-commutativity of (some definitions of) dict merging can be taken as a weak argument against using an operator, it is a *very* weak argument.

If I were to argue against an operator, I'd be more inclined to argue the more general point "operator overloading considered harmful" than "merging dicts is non-commutative".


> 3. However, like other mutating methods on builtin types, dict.update
> does *not* return a reference to the original object (this is
> deliberate, to encourage the treatment of containers as immutable when
> using a functional programming style)
>
> 4. Thus, just as sorted() was added as a functional counterpart to
> list.sort, is there something that can be added as a functional
> counterpart to dict.update?

At this point, I'd begin to think of a built-in updated function. Here's one possible implementation, slightly different from yours below:

def updated(*mappings, **kw):
     new = {}
     for mapping in mappings + (kw,):
         new.update(mapping)
     return new


Given how trivial is it, I'm not sure it's even worthwhile. But then, prior to sorted and reversed being added as built-ins, people argued that they were "too trivial" too. On the third hand, the existence of two, or more, implementations with subtly different semantics is an argument on picking the most useful version and putting it in the standard library.

So I'm now +0 for a built-in updated and +0.5 for collections.updated.



[...]
>      def copy_and_update(base, *others):
>          result = base.copy()
>          for other in others:
>              result.update(other)
>          return result
>
> That last alternative also suggests possible names for a standard
> library function (collections.copy_and_update) or a new instance
> method (dict.copy_and_update and collections.Mapping.copy_and_update).

But we don't write "copy_and_sort(L)", we write "sorted(L)".

It's enough to document that the function return a new dict, no need to explicitly state it copies its arguments. It's possible to be *too explicit* :-)



-- 
Steven


More information about the Python-ideas mailing list