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

Steven D'Aprano steve at pearwood.info
Fri Mar 1 05:44:27 EST 2019


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.


-- 
Steven


More information about the Python-ideas mailing list