Python3 - Prime Sieve
Your style generally looks fine. Do run flake8 and make the edits suggested, mostly adjusting whitespace. Starting out with zero and one, then switching to False, is a bit odd.

In e.g. last line of odd_sieve(), consider turning that into a three-line list comprehension.

    sieve = [False, False, True] + [True, False] * (n // 2 - 1)


That seems to allocate lots more than sqrt(N) entries.

The function name of number_of_odd_nums_below() appears to be off-by-one, as it's missing a comment explaining one may only pass in odd numbers. With such a restriction it seems that greatest_odd_number_below() would simply become n - 2.

My personal preference is for the naive code. The fancier version feels like it's cheating. If we get to use long division, why stop at 2? Why not 3, 5, 7, 11? When (before sqrt(N)) is the right place to stop, and transition to sieving? The advantage of the sieve is that, once having constructed a bounded list of primes, it can rapidly answer lots of prime queries. It would be interesting to see a particular problem domain involving many repeated queries where a natural tradeoff really does emerge.