Search code examples
pythonstringloopsenumeratecapitalize

Conceputalize breaking down a list comprehension of enumerate


I'm starting out here and love list comprehension. I found this following line of code for a practice problem of capitalizing every other letter in a string and it makes sense to me. The problem I'm hoping to get help with is figuring out how to write this code normally (for loop, if statements, etc. Beginners stuff) without list comprehension.

Here's the code that I started with that I want to break down:

s = input('Please enter a string: ')
answer = ''.join(char.upper() if idx % 2 else char.lower() for idx, char in enumerate(s))
print(answer)

And here is what I thought was a proper code to reproduce what this one above was doing:

s = input('Please enter a string: ')
for idx, char in enumerate(s):
    if idx % 2:
        s = char.upper()
    else:
        s = char.lower()
    answer = ''.join(s)
print(answer)

If I were to type Hello, I should get hElLo, but instead I get o

I'd appreciate any advice or tips on how to proceed here. Thanks!


Solution

  • Technically you're using a generator expression, and not a list comprehension. But the result is similar in this case.

    You're reading input into s, but then you're reassigning s in each iteration, and then you're joining s.

    You need to have a different variable. For the input and for the capitalized list. What a generator expression does is to return one value at a time, instead of building the whole capitalized list at once. But here you will need to declare it.

    Also, it doesn't make sense to assign answer in each iteration, the join is the last thing you need to do, once your capitalized list is prepared.

    This should work:

    toCapitalize = input('Please enter a string: ')
    capitalizedList = []
    
    for idx, char in enumerate(toCapitalize):
        if idx % 2:
            capitalizedList.append(char.upper())
        else:
            capitalizedList.append(char.lower())
    
    answer = ''.join(capitalizedList)
    print(answer)
    

    In case this helps, I've tried to reflect what line goes with what part of the generator expression below:

    for idx, char in enumerate(toCapitalize):            # for idx, char in enumerate(s)
        if idx % 2: capitalizedList.append(char.upper()) # char.upper() if idx % 2
        else: capitalizedList.append(char.lower())       # else char.lower()
    answer = ''.join(capitalizedList)                    # answer = ''.join()
    

    Again, the capitalizedList variable is implicit in the generator expressions or list comprehensions.

    Generator expressions

    To understand generator expressions look at this code:

    capitalize = 'hello'
    generator = (char.upper() if idx % 2 else char.lower() for idx, char in enumerate(capitalize))
    print(next(generator)) # h
    print(next(generator)) # E
    print(next(generator)) # l
    print(next(generator)) # L
    print(next(generator)) # o
    print(next(generator)) # raises a StopIteration exception, we've reached the end.
    

    Each call to next() calculates the result of the next iteration on the fly. Which is more memory efficient than building a whole list at once when you have big lists. In your case, the call to join() consumes all the generator, and joins the values returned.

    As a list comprehension

    Your code as a list comprehension would be:

    s = input('Please enter a string: ')
    answer = ''.join([ char.upper() if idx % 2 else char.lower() for idx, char in enumerate(s) ])
    print(answer)
    

    As I explained above, the difference is that here we're building a whole list at once, the generator is only returning one value at a time.

    Your code as a generator

    And finally, to be technically correct your code would be equivalent to this:

    def generator(s):
        for idx, char in enumerate(s):
            if idx % 2:
                yield char.upper()
            else:
                yield char.lower()
    
    answer = ''.join(generator(s))
    print(answer)
    

    That's how you build a generator in Python.