
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.