Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Underdocumented and partially untested behavior of set-like dict views #96408

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
filiplajszczak opened this issue Aug 30, 2022 · 5 comments
Closed

Comments

@filiplajszczak
Copy link
Contributor

Unlike sets, set-like dict view objects behave identical with both operator and non-operator versions of set operations.

In case of sets only non-operator versions accept any iterable as an argument, and one has to explicitly convert iterable to set to use operator on it. That's documented and tested.

s = {"a", "b"}
s.union(["a", "c"])  # valid
s | ["a", "c"]       # raises TypeError

Set-like dict view objects, however, accept any iterable after an operator.

d = {"a": "A", "b": "B"}
d.keys() | ["a", "c"]     # valid
d.items() | [("c", "C")]  # valid

As far as we were able to find that difference in behavior is not documented but is tested for keys but not for items. It's also reflected in the typeshed stubs.

@rhettinger
Copy link
Contributor

The dict view docs intended cover these behaviors in the final sentence:

For set-like views, all of the operations defined for the abstract base class collections.abc.Set are available (for example, ==, <, or ^).

However, if you didn't already know the differences between concrete set API and the abstract Set API, I could see how this particular point is easily missed, so perhaps an extra sentence highlighting the difference would be warranted:

Those operations difference from the corresponding set operations by supporting any iterable as a input.

As you say, the tests need to be beefed-up. They test registration with KeysView, ValuesView, and ItemsView but do not test compliance with those APIs.

Would you like to submit a PR?

filiplajszczak added a commit to filiplajszczak/cpython that referenced this issue Aug 30, 2022
Usage of set-like item dict views tested with operators and iterables.

Co-authored-by: Piotr Kaznowski <[email protected]>
caseneuve pushed a commit to caseneuve/cpython that referenced this issue Aug 30, 2022
While using set operators, set-like views accept any iterable as the
other operand, unlike sets which only accept sets as the input.

Co-authored-by: Filip Łajszczak <[email protected]>
caseneuve added a commit to caseneuve/cpython that referenced this issue Aug 30, 2022
While using set operators, set-like views accept any iterable as the
other operand, unlike sets which only accept sets as the input.

Co-authored-by: Filip Łajszczak <[email protected]>
caseneuve added a commit to caseneuve/cpython that referenced this issue Aug 31, 2022
While using set operators, set-like views accept any iterable as the
other operand, unlike sets which only accept sets as the input.

Co-authored-by: Filip Łajszczak <[email protected]>
@rhettinger
Copy link
Contributor

I've just reconsidered having an example and now think it should be taken out. It was correct to document that iterables were supported but we don't want an example to encourage it. It's too easy to fall into keys | 'abc' instead of keys | ['abc'].

@filiplajszczak
Copy link
Contributor Author

filiplajszczak commented Aug 31, 2022

If I understand correctly, it (possibility of easy error) was the reason to make it impossible for sets.

Maybe it could be possible, to replace keys example with items one with tuple of tuples instead of list of strings as the other operand.

@caseneuve
Copy link
Contributor

Aren't we heading into a more general discussion now (where my opinion would be: if an API is exposed, it should be accessible and documented; if, on the other hand, it's considered harmful, it shouldn't be exposed...)?

Just for the background of this ticket (it's a real life scenario, so please bare with me): we had a bit of code in our codebase where we used a sorted(a_dict.keys()) bit; then we discovered that those keys don't cover the data we need, and we wanted to extend it like this sorted(a_dict.keys() + ['missing', 'data']), which obviously failed. Then we tried this (it was an intuitive call): sorted(a_dict.keys() | ['missing', 'data']) which worked well and seemed somewhat elegant, but we realized that we can't find neither docs for this behavior, nor examples. Thus we went back to sorted(list(a_dict.keys()) + ['missing', 'data']), which -- it's a matter of personal opinion, I guess -- is not so elegant, but we were doc-covered at least. TL;DR: discovering an elegant and concise API that suited our need but was hard enough to find in the documention was quite confusing and upsetting :)

@pochmann
Copy link
Contributor

pochmann commented Sep 2, 2022

@caseneuve Just alternatives for your example:

sorted(a_dict.keys() | {'missing', 'data'})
sorted(set(a_dict) | {'missing', 'data'})
sorted([*a_dict, 'missing', 'data'])
sorted({*a_dict, 'missing', 'data'})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants