<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p>Hi,</p>
    <p>I'm not old on this list but every time there is a proposal, the
      answer is "what are you trying to solve ?".</p>
    <p>Since</p>
    <p><code><span class="pln">z </span><span class="pun">=</span><span
          class="pln"> </span><span class="pun">{**</span><span
          class="pln">x</span><span class="pun">,</span><span
          class="pln"> </span><span class="pun">**</span><span
          class="pln">y</span><span class="pun">} and z.update(y)
          Exists, I can"t find the answer.<br>
        </span></code></p>
    <p><code><span class="pun"><br>
        </span></code></p>
    <br>
    <div class="moz-cite-prefix">Le 02/03/2019 à 04:52, Steven D'Aprano
      a écrit :<br>
    </div>
    <blockquote type="cite"
      cite="mid:20190302035224.GO4465@ando.pearwood.info">
      <pre wrap="">Executive summary:

- I'm going to argue for subclass-preserving behaviour;

- I'm not wedded to the idea that dict += should actually call the 
update method, so long as it has the same behaviour;

- __iadd__ has no need to return NotImplemented or type-check its 
argument.

Details below.


On Fri, Mar 01, 2019 at 04:10:44PM -0800, Brandt Bucher wrote:

[...]
</pre>
      <blockquote type="cite">
        <pre wrap="">In your Python implementation samples from the PEP, dict subclasses will
behave differently from how list subclasses do. List subclasses, without
overrides, return *list* objects for bare "+" operations 
</pre>
      </blockquote>
      <pre wrap="">
Right -- and I think they are wrong to do so, for reasons I explained 
here:

<a class="moz-txt-link-freetext" href="https://mail.python.org/pipermail/python-ideas/2019-March/055547.html">https://mail.python.org/pipermail/python-ideas/2019-March/055547.html</a>

I think the standard handling of subclasses in Python builtins is wrong, 
and I don't wish to emulate that wrong behaviour without a really good 
reason. Or at least a better reason than "other methods break 
subclassing unless explicitly overloaded, so this should do so too".

Or at least not without a fight :-)



</pre>
      <blockquote type="cite">
        <pre wrap="">(and "+=" won't call an overridden "extend" method).
</pre>
      </blockquote>
      <pre wrap="">
I'm slightly less opinionated about that. Looking more closely into the 
docs, I see that they don't actually say that += calls list.extend:

s.extend(t)       extends s with the contents of t (for
or s += t         the most part the same as s[len(s):len(s)] = t)

<a class="moz-txt-link-freetext" href="https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types">https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types</a>

only that they have the same effect. So the wording re lists calling 
extend certainly needs to be changed. But that doesn't mean that we must 
change the implementation. We have a choice:

- regardless of what lists do, we define += for dicts as literally 
  calling dict.update; the more I think about it, the less I like this.

- Or we say that += behaves similarly to update, without actually 
  calling the method. I think I prefer this.

(The second implies either that += either contains a duplicate of the 
update logic, or that += and update both delegate to a private, C-level 
function that does most of the work.)

I think that the second approach (define += as having the equivalent 
semantics of update but without actually calling the update method) is 
probably better. That decouples the two methods, allows subclasses to 
change one without necessarily changing the other.


</pre>
      <blockquote type="cite">
        <pre wrap="">So a more analogous
pseudo-implementation (if that's what we seek) would look like:

def __add__(self, other):
    if isinstance(other, dict):
        new = dict.copy(self)
        dict.update(new, other)
        return new
    return NotImplemented
</pre>
      </blockquote>
      <pre wrap="">
We should not require the copy method.

The PEP should be more explicit that the approximate implementation does 
not imply the copy() and update() methods are actually called.


</pre>
      <blockquote type="cite">
        <pre wrap="">def __iadd__(self, other):
    if isinstance(other, dict):
        dict.update(self, other)
        return self
    return NotImplemented
</pre>
      </blockquote>
      <pre wrap="">
I don't agree with that implementation.

According to PEP 203, which introduced augmented assignment, the 
sequence of calls in ``d += e`` is:

1. Try to call ``d.__iadd__(e)``.

2. If __iadd__ is not present, try ``d.__add__(e)``.

3. If __add__ is missing too, try ``e.__radd__(d)``.

but my tests suggest this is inaccurate. I think the correct behaviour 
is this:

1. Try to call ``d.__iadd__(e)``.

2. If __iadd__ is not present, or if it returns NotImplemented, 
   try ``d.__add__(e)``.

3. If __add__ is missing too, or if it returns NotImplemented,
   fail with TypeError.

In other words, e.__radd__ is not used.

We don't want dict.__iadd__ to try calling __add__, since the later is 
more restrictive and less efficient than the in-place merge. So there is 
no need for __iadd__ to return NotImplemented. It should either succeed 
on its own, or fail hard:

def __iadd__(self, other):
    self.update(other)
    return self

Except that the actual C implementation won't call the update method 
itself, but will follow the same semantics.

See the docstring for dict.update for details of what is accepted by 
update.


</pre>
    </blockquote>
    <br>
  </body>
</html>