Finding all factors of n efficiently
For big numbers and for many numbers checking for divisibility with prime numbers under the sqrt will give a big speed increase, you could still use the same method. But there are many ways to optimize factorization, many many ways. 



I like your code! But some styling notes and then a suggestion. 
I think the usual way to check if a number is a perfect square is:

if root*root == n:
        yield int(root)
        start += step


the check:

root.is_integer()


is more implicit and explicit is better. 



Now, in line comments is hard to read. You shouldn't use those.

if n % 2 == 0:
    step = -1  # n is even, so check both evens and odds
else:
    step = -2  # n is odd, so check only odds
    start = start // 2 * 2 + 1  # Round start to odd number


instead: 

if n % 2 == 0:
    # n is even, so check both evens and odds
    step = -1  
else:
    # n is odd, so check only odds
    step = -2  
    # Round start to odd number
    start = start // 2 * 2 + 1  


but even better would be to not repeat what the if statement already says.  

if n % 2 == 0:
    # only check evens
    step = -1  
else:
    # check only odds
    step = -2  
    # Round start to odd number
    start = start // 2 * 2 + 1 




start = start // 2 * 2 + 1


might be implicit, could be more readable:

start = int(root)
start += 1 if start % 2 == 0 else 0




Now to the suggestion 

If with any number that is even, it has a number of factors 2. So remove the factors 2 and do you algorithm on that number. Like this:

def factors(n):
    """
    Generates all factors of n. Iterates from sqrt(n) down to zero.
    When a factor x is found, another factor must be n divided by x.
    If n is odd, only odd numbers need to be checked.

    &gt;&gt;&gt; sorted(list(factors(1001)))
    [1, 7, 11, 13, 77, 91, 143, 1001]
    """

    root = sqrt(n)
    step = -2
    start = int(root)
    start += 1 if start % 2 == 0 else 0

    if root*root == n:
        yield int(root)
        start += step

    for x in range(start, 0, step):
        if n % x == 0:
            yield x
            yield n // x

def brigde(n):
    twos = 1
    while n % 2 == 0:
        twos += 1
        n //= 2

    for num in factorss(n):
        for i in range(twos):
            yield num*(2**i)


You'll have to multiply each of the factors returned with the factors of two that the original number contained. But you remove the need to check even numbers, ending up with only the faster of the two ways your code can go.