
On Dec 3, 2019, at 02:00, Serhiy Storchaka <storchaka@gmail.com> wrote:
I argued for | and |= as lesser evil. But there may be a problem. Dict and dict keys view are interchangeable in the context of some set operations: "in" checks for existence of the key and iterating yields keys. Currently both `dictkeys | dict` and `dict | dictkeys` return the same, the set containing the union of keys.
This is all just because a dict is an iterable of, and container of, its keys. It’s not a set of them the way its keys view is, but it doesn’t have to be. And you don’t need to do anything special to preserve that, just make sure dict.__or__ and __ror__ don’t try to handle sets (or arbitrary iterables), only mappings (or only dicts), and the set or view implementation will still work.
{1: 2}.keys() | {3} {1, 3} {3} | {1: 2}.keys() {1, 3}
What it will return if implement | for dicts? It should be mentioned in the PEP. It should be tested with a preliminary implementation what behavior is possible and more natural.
What is there to document or test here? There’s no dicts involved in either operator, only a set and a key view, both of which are set types and implement set union. I think the question you wanted to ask is | between a dict and a set or view, not between two sets or views. But as I said above, there’s an obvious right thing to do there, and the obvious implementation does that. Of course it’s still worth writing the tests, as well as the other usual __rspam__ tests that go with every operator on the builtins.
## Other objections
The principal question about the result type was not mentioned above. `dict | dict` should return an exact dict for dict subclasses for the same reasons as not including __or__ in the Mapping API. We cannot guarantee the signature and the behavior of the constructor and therefore we have no way to create a copy as an instance of general dict subclass. This is why `dict.copy()` returns an exact dict. This is why `list + list`, `tuple + tuple`, `str + str`, `set | set`, `frozenset | frozenset`, etc, etc return an instance of the base class. This is why all binary operators for numbers (like `int + int`, `float * float`) return an instance of the base class.
Also because if you want MyInt + int and int + MyInt to return a MyInt, you can do that trivially (as a subclass your __ror__ always gets precedence over the base __or__), and if you want MyDicf.copy() to return a MyDict it’s even easier (just override copy), so there’s no reason for the base class to even try to do that for you. So I agree, dict.__or__ should return a dict; anyone who wants MyDict to return a MyDict can just override, as with every other operator on the builtins. More generally, I think the design should follow all the other operators on builtins on all such questions. For example, should dict.__or__ and __ior__ handle all the same values as update, or some more restricted set of types? The same as set.__or__ and __ior__ vs. union, and list.__add__ and __iadd__ vs extend, and so on. If they’re all consistent, and there’s no compelling reason to add an inconsistency here, don’t.