shuberman
shuberman

Reputation: 1490

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.

Upvotes: 4

Views: 1913

Answers (3)

S.B
S.B

Reputation: 16526

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)
while True:
    try:
        i = next(it)
    except StopIteration:
        break
    else:
        print(i ** 2)

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

Upvotes: 4

dumbass
dumbass

Reputation: 27214

The expression "" in s returns True for all strings s, because the empty string is trivially contained in all strings: the expression A in B is akin to asking the question β€˜is there a pair of indices i and j such that B[i:j] == A’. If A is the empty string, we can always just set both i and j to zero, so the answer is yes.

However, iterating over code points of the empty string does not yield "", it yields nothing at all. Observe:

def iterate(what):
    print("iterating over {}".format(repr(what)))
    for item in what:
        print("item: {}".format(repr(item)))
    print("iteration done")
    print()

iterate("abc")
iterate("πŸ‡ͺπŸ‡Ί")
iterate("")

The above code will print:

iterating over 'abc'
item: 'a'
item: 'b'
item: 'c'
iteration done

iterating over 'πŸ‡ͺπŸ‡Ί'
item: 'πŸ‡ͺ'
item: 'πŸ‡Ί'
iteration done

iterating over ''
iteration done

In your example, the loop body will never run, so it will never increment the vowel counter.

Upvotes: 0

Elisha Kupietzky
Elisha Kupietzky

Reputation: 114

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.

Upvotes: 3

Related Questions