How is an iterable created when the first argument to sum includes a for loop?

Viewed 45

The Python documentation for sum shows that an iterable has to be passed in as the first argument for sum.

I was solving this leetcode problem 1295. Find Numbers with Even Number of Digits :

Given an array nums of integers, return how many of them contain an even number of digits.

I was solving this the long way like this:

# # longer as in more lines of code
class Solution:
    def findNumbers(self, nums: List[int]) -> int:
        count = 0

        for num in nums:
            if len(str(num)) % 2 == 0:
                count += 1

        return count

but decided to look at the other suggested answers to see if I missed something. One never knows with Easy questions on Leetcode. That's when I ran into this line of code that made me wonder if I understood what was going on.

from typing import List
# one line solution
class Solution:
    def findNumbers(self, nums: List[int]) -> int:
        return sum(len(str(num)) %2 == 0 for num in nums)

Could someone clarify how "future" booleans lead to an iterable? Is a list being created from the for loop?

2 Answers

That's not something special to sum. len(str(num)) %2 == 0 for num in nums is a generator expression, it's like list comprehension but does not create a list. Instead an iterator that calculates values on demand when they're needed.

Notice that replacing the generator expression with a list comprehension gives the same result:

        return sum([len(str(num)) %2 == 0 for num in nums])

But leads to more memory usage.

Then there is what Samwise mentioned, True == 1 and False == 0. You can change your code to use this fact

class Solution:
    def findNumbers(self, nums: List[int]) -> int:
        count = 0

        for num in nums:
            count += len(str(num)) % 2 == 0

        return count

The ... for ... expression inside the sum() is a grammar sugar to create annoymous generator in python (see here for more details). The equlivent code shall be


def f(num):
    for num in nums:
       yield len(str(num)) %2 == 0

class Solution:
    def findNumbers(self, nums: List[int]) -> int:
        return sum(f(nums))
Related