Thank you to everyone for your patience while I dragged my feet 
responding to this. It's not because of a lack of interest, just a lack 
of uninterrupted time to focus on this :-)

I believe that the final sticking points are the behaviour with 
subclasses and whether or not the union operator ought to call "copy" on 
the left hand operator, or directly on dict.

That is, the difference is between *roughly* these two:

    new = self.copy()  # Preserves subclasses.
    new = dict.copy(self)  # Always a builtin dict

(But we should use a __copy__ dunder rather than copy.)

TL;DR:

My *strong* preference is for the union operator to call the left hand 
operand's copy method, in order to preserve the subclass of the LH 
operand, but if that's a sticking point for the proposal I'll accept the 
alternative, non-preserving, behaviour.


On Thu, Feb 06, 2020 at 07:38:03PM -0000, Brandt Bucher wrote:
> One issue that's come up during PR review with Guido and Serhiy is, 
> when evaluating `a | b`, whether or not the default implementation 
> should call an overridden `copy()` method on `a` in order to create an 
> instance of the correct subclass (rather than a plain-ol' `dict`). For 
> example, `defaultdict` works correctly without any additional 
> modification:

My opinion is that Python built-in types make subclassing 
unnecessarily(?) awkward to use, and I would like to see that change. 
For example, consider subclassing float. If you want your subclass to be 
actually usable, you have to write a whole bunch of boilerplate, 
otherwise the first time you perform arithmetic on your subclass, it 
will be converted to a regular old float.

    class MyFloat(float):
        # This is the only thing I actually want to change.
        def mymethod(self): pass

        # But I need to override every operator too.
        def __add__(self, other):
            tmp = super().__add__(other)
            if tmp is NotImplemented:
                return tmp
            return MyFloat(tmp)
        # and so on for all the other operators

This is painful and adds a great amount of friction to subclassing.

A more pertinent example, from dict itself:

    py> class MyDict(dict):
    ...     pass
    ...
    py> d = MyDict()
    py> type(d.copy())
    <class 'dict'>

So unless my subclass overrides the copy method, copy doesn't actually 
makes a copy, it coerces the copy into the superclass.

My preference is to avoid inflicting that pain onto subclasses.

Serhiy commented:

"We can discuss and change the standard handling of subclasses in Python
builtins. But if it be changed, it should be changed for all builtin
classes, without exceptions and special cases."

Changing all builtins is a big, backwards-incompatible change. It 
probably should have been done in 3.0. If it is done, it would surely 
need to be done using a `__future__` import. So in practice, it will 
probably never be done.

My personal opinion is that I would rather have the inconsistency 
between dict union operator and the rest of the builtins. Better to have 
one class do the Right Thing (in my opinion) than none of them. But I 
accept that others may disagree, and if this issue is a sticking point, 
I'll back down gracefully and go with the non-preserving behaviour.

Of course, if the status quo where builtin methods and operators return 
the builtin class unless explicitly overridden in the subclass is an 
intentional design choice for a good reason, that may cast a different 
light on this issue.

If we do decide to delegate to the operands for making a copy, we 
should follow Brett's comment:

"I agree that if we want to go down the road of creating a copy to allow 
for subclasses then we should define a dunder method for such a use, 
even if it's redundant in the face of dict.copy()."

In other words:

* dict gains a `__copy__` dunder
* `dict.copy` becomes an alias to `dict.__copy__`
* dict union operator should call the `__copy__` dunder 
  of the left hand operand (rather than `copy` itself).

I believe that will do the right thing in all cases. Have I missed 
anything?


[Brandt]
> So this has immediate benefits for both usability and maintenance: 
> subclasses only need to override `copy()` to get working 
> `__or__`/`__ror__` behavior.

Indeed, except it should be the dunder `__copy__`.



-- 
Steven
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/SAJYB7DFLGIDFNIBWNGP7OAOSLHXREKX/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to