2

I am running some Python3 code using the IDLE and I would like to understand why for

a = {'a':1,'b':2}

both:

a.keys()-'a'

(not even sure why this one works) and

a.keys()-{'a'}

produce the same result.


Weirdly enough, the second option seems produce different outputs once the operation is performed inside a function (and call from the IDLE) or directly run from the IDLE...

Diego F Medina
  • 429
  • 3
  • 14

2 Answers2

2

Strings are iterables of characters – set('abc') == {'a', 'b', 'c'}. The - operator for a dict’s keys accepts any iterable, so 'a' is equivalent to {'a'} there.

If you used a longer string, it wouldn’t appear to work anymore.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • It might be clearer if you mentioned that the documentation for `dict.keys` says that it returns a set-like object of the dictionary's keys. Then it's simple set operations, as you've demonstrated. – Reti43 Oct 16 '17 at 10:44
  • Is 'a' not a "char"? In fact: a.keys()-chr(97) produces the same output. – Diego F Medina Oct 16 '17 at 10:46
  • Diego when you call `dict.keys()` you are getting a `dict_keys` instance while the other is a `set` instance. The `__sub__` method is defined for one of them but not for the other. – Adirio Oct 16 '17 at 10:51
  • @Adirio yes I know thanks. This is just to note that Reti43's comment "simple set operations" might not apply. – Diego F Medina Oct 16 '17 at 10:57
  • 1
    @DiegoFMedina: Python doesn’t have a character type. `chr` returns a single-character string. And you’re correct that `{'a', 'b'} - 'a'` producing an error is why I said “operator for a dict’s keys” and not “operator for a set” like Reti43 kind-of-suggested. =) – Ry- Oct 16 '17 at 11:02
  • Fair enough. When I think minus in terms of sets, I think of `set.difference()`, which can handle iterables. I wasn't aware that `set.__sub__()` works only for other sets. – Reti43 Oct 16 '17 at 19:37
  • I would say the fact that `__sub__` on dict_keys doesn't behave like Set most definitely seems like an oversight and a bug. The fact that you can't do `set() - list()`, but can do `set().difference(list())` is on purpose and by design. It prevents accidentally passing the wrong type, exactly like the example above where the person passed a string (which is technically iterable). dict_keys should behave like set and allow arbitrary iterable. It's especially bad since `dict().keys() - list()` returns a set, so you can't even do `dict().keys() - list() - list()`, that just seems broken to me. – Ehsan Kia Nov 10 '21 at 20:59
1

The thing to understand here is the kind of entities you are subtracting.

a.keys() is an iterable. Which simply means that it can be iterated through using a for or iter. Formally speaking , in python anything is an iterable that implements __iter__ function. Check this link

>> type(a.keys)
>> dict_keys

And dict_keys is an iterable, how do you know that? because it has an attribute __iter__.

>> hasattr(a.keys() , '__iter__')
>> True

a.keys() returns a view object. Which allows you to subtract other iterables from itself. As stated in the docs:

dictview - other
Return the difference between the dictview and the other object (all elements in dictview that aren’t in other) as a new set.

So, to subract something from a.keys() , that entity should be an iterable too.

In your case :

  1. a.keys() - 'a' works because strings are iterables in python

    >> hasattr('a' , '__iter__')

    >> True

    So, strings are iterables

  2. a.keys() - {'a'} works because {'a'} is a set, which is an iterable

    >> hasattr({'a'} , '__iter__')

    >> True

On the other hand if you try this:

>> a.keys() - 1
>> TypeError: 'int' object is not iterable

So, you cannot subtract two objects which are not iterables. Hope this helps :)

xssChauhan
  • 2,728
  • 2
  • 25
  • 36
  • 2
    It's incorrect to say that in general two iterables can be subtracted. `range` and `list` are iterables, but they can't be subtracted because they don't support the `__sub__` method. On the other hand, a set supports that method because it makes sense for that object. – Reti43 Oct 16 '17 at 11:01