You pass in a seed to the global random object each time you call f(), because all top-level functions in the random module feed into a singleton object. This means that by the time f3 is created, the seeds set in f2 and f1 have been superseded, the seeds are not independent from the global random object. Importing random again for each f() call does not give you a new state, as really only binds names anew each time once the module object itself is loaded (only on the first import).
If you want to have a seeded random generator per function, you need to create individual random.Random() instances:
import random
import time
def f(p):
seeded_random = random.Random(time.time())
def g():
return 1 if seeded_random.random() < p else 0
return g
From the random module documentation:
The functions supplied by this module are actually bound methods of a hidden instance of the random.Random class. You can instantiate your own instances of Random to get generators that don’t share state.
randommodule and therefore only one seed. You'll have to create arandom.Random(seed)object for each function.