4

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.

9
  • It contains no chars so the loop in count_vowels is exited immediately, eh? Commented Nov 15, 2020 at 8:23
  • @bipll That's fine but my question is why? Did you see the print expression? It returns True. So I am actually asking why the for loop skips a True expression ? Commented Nov 15, 2020 at 8:28
  • 1
    @mishsx iteation over strings iterates over the characters in a string. The empty string, by definition, has no characters. It's entire purpose is to serve as an empty version of a string. I suppose, there could be a convetion, like, the last character of a string will always be an empty string, but there is not such convention, and I see little value for it. In any case, it is simply not how it is defined to work. Commented Nov 15, 2020 at 8:35
  • 1
    And note, I suppose str objects have the strange characteristic that iterating over them produces objects of that same type, since python does not have a "char" type, it is simply length-1 strings. Commented Nov 15, 2020 at 8:38
  • 1
    @mishsx when you evaluate this expression as an argument to print you get its boolean value which is True. When you evaluate it inside a body loop you don't evaluate it once as the body is never entered as the string iterated over is empty. Commented Nov 15, 2020 at 10:03

3 Answers 3

4

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__)

Sign up to request clarification or add additional context in comments.

5 Comments

So you mean to say there is a try-catch mechanism python has in its for loop implementation that causes the code to skip the loop ? If so your answer will be considered accepted. But it would help if you provide any documentation link for proof.
Yes, there is a try-catch mechanism, but it isn’t the one this post presents. Look up StopIteration.
@mishsx yes. best proof is, trying to implement a sequence type yourself. As you know you have to implement __getitem__ method. Inside this function you have to somehow indicates that this is not a valid indices and in in order to do that, you raise an "IndexError" exception. But as a result you can see that it will be silenced by python. how ? cause there is a try-except mechanism that "For" handles that for you. (as I mentioned in the answer)
@SorousHBakhtiary Thank you sir. Your answer is much aprreciated. It captures the essence of what I was looking for.
@mishsx happy to help. In case you wanna implement an Iterator, you have to do the almost same thing(For does something else with Iterators) inside __next__ method. Instead, you raise an StopIteration. At the end it will be silenced again by For.
3

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.

3 Comments

I am not asking for a fix. I am asking why the function count_vowels("") skips the for loop ? when the standalone expression "" in "aeiouAEIOU" is True in itself. Try debugging in Pyton vizualiser and see for yourself. I'm surprised this has not been asked before on Stack Overflow
Thanks for clarifying.Because the length of s is 0 so there's nothing to iterate over.
Going by the unanimous answers and comments, I will have to consider that the behaviour is by design. I was questioning the design all along and not the code.
0

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.

1 Comment

That's an excellent example. Although the focus of my question is more towards how the expression " in "aeiouAEIOU" yields a True but not along with a for loop. However, from your example it seems it works differently with a for loop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.