[Python-ideas] Dict joining using + and +=

Neil Girdhar mistersheik at gmail.com
Fri Mar 1 09:38:04 EST 2019


On Friday, March 1, 2019 at 5:47:06 AM UTC-5, Steven D'Aprano wrote:
>
> On Fri, Mar 01, 2019 at 08:47:36AM +0200, Serhiy Storchaka wrote: 
>
> > Currently Counter += dict works and Counter + dict is an error. With 
> > this change Counter + dict will return a value, but it will be different 
> > from the result of the += operator. 
>
> That's how list.__iadd__ works too: ListSubclass + list will return a 
> value, but it might not be the same as += since that operates in place 
> and uses a different dunder method. 
>
> Why is it a problem for dicts but not a problem for lists? 
>
>
> > Also, if the custom dict subclass implemented the plus operator with 
> > different semantic which supports the addition with a dict, this change 
> > will break it, because dict + CustomDict will call dict.__add__ instead 
> > of CustomDict.__radd__. 
>
> That's not how operators work in Python or at least that's not how they 
> worked the last time I looked: if the behaviour has changed without 
> discussion, that's a breaking change that should be reverted. 
>
> Obviously I can't show this with dicts, but here it is with lists: 
>
> py> class MyList(list): 
> ...     def __radd__(self, other): 
> ...             print("called subclass first") 
> ...             return "Something" 
> ... 
> py> [1, 2, 3] + MyList() 
> called subclass first 
> 'Something' 
>
>
> This is normal, standard behaviour for Python operators: if the right 
> operand is a subclass of the left operand, the reflected method __r*__ 
> is called first. 
>
>
> > Adding support of new operators to builting 
> > types is dangerous. 
>
> Explain what makes new operators more dangerous than old operators 
> please. 
>
>
> > >    I do not understand why we discuss a new syntax for dict merging if 
> we 
> > >    already have a syntax for dict merging: {**d1, **d2} (which works 
> with 
> > >    *all* mappings). Is not this contradicts the Zen? 
> > > 
> > > 
> > >But (as someone else pointed out) {**d1, **d2} always returns a dict, 
> > >not the type of d1 and d2. 
> > 
> > And this saves us from the hard problem of creating a mapping of the 
> > same type. 
>
> What's wrong with doing this? 
>
>     new = type(self)() 
>
> Or the equivalent from C code. If that doesn't work, surely that's the 
> fault of the subclass, the subclass is broken, and it will raise an 
> exception. 
>
> I don't think it is our responsibility to do anything more than call 
> the subclass constructor. If that's broken, then so be it. 
>
>
> Possibly relevant: I've always been frustrated and annoyed at classes 
> that hardcode their own type into methods. E.g. something like: 
>
>     class X: 
>         def spam(self, arg): 
>             return X(eggs)   
>             # Wrong! Bad! Please use type(self) instead. 
>
> That means that each subclass has to override every method: 
>
>     class MySubclass(X): 
>         def spam(self, arg): 
>             # Do nothing except change the type returned. 
>             return type(self)( super().spam(arg) ) 
>
>
> This gets really annoying really quickly. Try subclassing int, for 
> example, where you have to override something like 30+ methods and do 
> nothing but wrap calls to super. 
>

I agree with you here.  You might want to start a different thread with 
this idea and possibly come up with a PEP.  There might be some pushback 
for efficiency's sake, so you might have to reel in your proposal to 
collections.abc mixin methods and UserDict methods.

Regarding the proposal, I agree with the reasoning put forward by Guido and 
I like it.  I think there should be:
* d1 + d2
* d1 += d2
* d1 - d2
* d1 -= d2

which are roughly (ignoring steve's point about types)
* {**d1, **d2}
* d1.update(d2)
* {k: v for k, v in d1.items() if k not in d2}
* for k in list(d1): if k not in d2: del d1[k]

Seeing this like this, there should be no confusion about what the 
operators do.

I understand the points people made about the Zen of Python.  However, I 
think that just like with lists, we tend to use l1+l2 when combining lists 
and [*l1, x, *l2, y] when combining lists and elements.  Similarly, I think 
{**d1, **d2} should only be written when there are also key value pairs, 
like {**d1, k: v, **d2, k2: v2}.

Best,

Neil


>
> -- 
> Steven 
> _______________________________________________ 
> Python-ideas mailing list 
> Python... at python.org <javascript:> 
> https://mail.python.org/mailman/listinfo/python-ideas 
> Code of Conduct: http://python.org/psf/codeofconduct/ 
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20190301/30743d79/attachment-0001.html>


More information about the Python-ideas mailing list