Why for loop can work with an empty set in all()?

Viewed 113

Python newbie here. So I'm reading Whirlwind Tour of Python

and there is an example:

def gen_primes(N):
    """Generate primes up to N"""
    primes = set()
    for n in range(2, N):
        if all(n % p > 0 for p in primes):
            primes.add(n)
            yield n

print(*gen_primes(100))

for this line: if all(n%p>0 for p in primes), I suppose primes is still empty, why for loop still working?

4 Answers

According to the all() documentation:

Return True if all elements of the iterable are true (or if the iterable is empty).

In your case the underlying generator yields an empty value, it's like all([]) or all(set()) thus an empty iterable passed into all() builtin.

You're most likely looking for this instead, so you first check if it's not empty, then check for all of the elements against some condition.

if primes and all(...):

Though as mentioned in the comments, in your particular code, that would cause if False, so you need to rewrite the implementation a bit or split the conditions into two IFs.


If you're into C, you might find the underlying all() implementation interesting, especially this part which I think simply exits the loop and thus causes return True.

Kind of like this:

for _ in iterable:
    # here check for false and return
# aaand the iterable is empty, so meh
# no loop and falls directly to the last return
return True

That is because of the definition of the quantifier that it represent, in the case of all is the for all quantifier, by definition the quantifier should return the neutral element of the operator when apply to an empty set, the operator of the for all is the and operator and its neutral is True (True and X == X), same deal for any that is the quantifier exist, which operator is or and its neutral is False (False or X == X)

all([])==True is vacuously true that all element in this empty set of thing comply with whatever condition

any([])==False no element in this empty set of thing comply with whatever condition

I'm adding a pseudo-code trying to explain that a "neutral" value (does not change the result) is the correct result for doing a mathematical operation (all from this question is a logical conjuction) on empty input:

  • 0 + numX = numX, that's why sum(<empty>) = sum(0, <empty>) = sum(0) = 0
  • 1 * numY = numY, that's why product(<empty>) = product(1, <empty>) = product(1) = 1
  • True and boolX = boolX, that's why all(<empty>) = all(True, <empty>) = all(True) = True
  • False or boolY = boolY, that's why any(<empty>) = any(False, <empty>) = any(False) = False
#This doesn't print anything
for p in set():
  print('p', p)

# This yields an empty list
print([n % p > 0 for p in primes])

# This returns true (primes = set())
print(all([n % p > 0 for p in primes]))

# This also returns true
print(all([n % p > 0 for p in []]))

# This returns false
print(any([n % p > 0 for p in []]))

print(all([True]), all([False]))

print(all([]))

print(all([True, True]), all([True, False]))

Output of the above code lines-

Output

So it's not about all() it's that all() returns True or False based on the argument passed, in your case all([n % p > 0 for p in primes]) is an empty list hence it would return True and if condition is always True!.

Instead you can modify condition to check for the length len([n % p > 0 for p in primes]) and all(n % p > 0 for p in primes) to get correct response.

Related