Why does for loop exit for "" (empty string) in python?

To explain my query I have a simple code snippet below followed by my question.

def count_vowels(s):
    num_vowels = 0
    for char in s:
        if char in 'aeiouAEIOU':
             num_vowels = num_vowels + 1
    return num_vowels

print(count_vowels(""))
print("" in "aeiouAEIOU")

gives an output

0 
True

My doubt:

Why does an empty string "" returns True for the expression

"" in "aeiouAEIOU"

But it skips when it is present along with a for loop?

for char in s:  

My understanding is that empty strings are a subset of all strings then why it is ignored when the same expression is in the for loop? Feel free to correct me if there is something I am missing here.


Solution 1:

Your understanding is correct: "empty strings are a subset of all strings"

But now let's see what happens when we use for for a sequence type such as string. Let's say we have:

lst = [1, 2, 3, 4, 5]

for i in lst:
    print(i ** 2)

You can just think that it turns into:

index = 0
while True:
    try:
        i = lst.__getitem__(index)
    except IndexError:
        break
    print(i ** 2)
    index += 1

In your Example, when it tries to get even the first item, it will raise an Exception and break out of the loop. So it doesn't even go inside For loop.

I said "just think" because in for-loop, iter() is get called on the object (here lst) and this built-in function will get an iterator out of the object. In order this to be happened the object should implement either the iterable protocol which is either __iter__ or it must support the sequence protocol (the __getitem__())).

lst = [1, 2, 3, 4, 5]
it = iter(lst)
try:
    while i := next(it):
        print(i ** 2)
except StopIteration:
    pass

Both str and list object have __iter__ so that is the method gets called rather than __getitem__. (__iter__ has precedence over __getitem__)

Solution 2:

In the second print command you're asking does "" appear in "aeiouAEIOU" and that is True. However, the length of "" is 0. So the for loop doesn't execute even once since there are no items to iterate over.