Python: Automatically change string to set in .intersection() method of Set?

Viewed 94

I am new to Python (and have basic knowledge of Java) and learning it on my own at the moment by using the Head First Python, 2nd Edition book.

In one of the examples in the book, it shows how the .intersection() method of Sets is used. It does so in the following way:

if __name__ == '__main__':
    def search_for_vowels(word: str) -> set:
        """Return any vowels found in supplied word."""
        vowels = set('aeiou')
        return vowels.intersection(set(word))

    print(search_for_vowels('hitch-hiker'))
    print(search_for_vowels('sky'))

However, when I tried this on my own, I accidentally forgot the 'set' part in the above code (above the print statements), hence it became:

if __name__ == '__main__':
    def search_for_vowels(word: str) -> set:
        """Return any vowels found in supplied word."""
        vowels = set('aeiou')
        return vowels.intersection(word)

    print(search_for_vowels('hitch-hiker'))
    print(search_for_vowels('sky'))

The code, however, did run without any problems and returned the correct output. This seems kind of strage to me, as I would be comparing a set to a string, and not a set to a set. My question therefore is: Does Python automatically convert a string to a set when performing the intersection() method of Set?

Thanks in advance, David

3 Answers

From the official docs, some set methods, including intersection(), accept an iterable for other:

Note, the non-operator versions of union(), intersection(), difference(), symmetric_difference(), issubset(), and issuperset() methods will accept any iterable as an argument. In contrast, their operator based counterparts require their arguments to be sets. This precludes error-prone constructions like set('abc') & 'cbs' in favor of the more readable set('abc').intersection('cbs') .

and

Note, the non-operator versions of the update(), intersection_update(), difference_update(), and symmetric_difference_update() methods will accept any iterable as an argument.

Emphasis mine. No conversion may be happening (I haven't checked the source code).

Operator vs Non-operator:

From the docs:

union (*others )
set | other | ...
Return a new set with elements from the set and all others.

intersection (*others )
set & other & ...
Return a new set with elements common to the set and all others.

So | is the operator for union(), and & is the operator for intersection().

Examples:

>>> # using the & operator, with a set
>>> set('abc') & set('cbs')
{'c', 'b'}
>>>
>>> # using the intersection method, with a set
>>> set('abc').intersection(set('cbs'))
{'c', 'b'}
>>>
>>> # using the & operator, with non-set (error)
>>> set('abc') & 'cbs'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for &: 'set' and 'str'
>>>
>>> # using the intersection method, with non-set (okay)
>>> set('abc').intersection('cbs')
{'c', 'b'}
>>>
>>> # also applies to general operations, see link below
>>> # using the + operator with two integers
>>> 4 + 5
9
>>> # using the __add__ method
>>> (4).__add__(5)
9

See this list of operators and their methods/functions. Some built-in types, like sets and dictionaries, each have their own operator mapping as well. And using the __dunder__ (double-underscore x2) magic methods, these can be implemented for user-defined objects as well.

The non-operator version accepts any iterable as an argument. By non-operator, I mean functional implementation of these operations like .intersection(), .union(), e.t.c.

If you want to use the operator version of this operation, all the variables should be set.

For Example -> set1 & set2 requires both the operands of type set.

The intersection method requires an iterable as a parameter.

You are passing a string(word) which is an iterable, hence it works successfully.

Related