After some search, I figure out the answer myself.
Essentially, circular import is a problem for from … import … because it returns the imported module only after the module code is executed.
To illustrate, assume we have import a in b.py and import b in a.py. For import a and import b, I simply look up sys.modules to find a/b, just put a new module in and return.
On the other hand, from a import c in b.py has code like this (pseudo python, similar for from b import d in a.py)
if 'a' not in sys.modules:
sys.modules[a] = (A new empty module object)
run every line of code in a.py
add the module a to its parent's namespace
return module 'a'
we start from a.py, put an empty module for b to sys.modules and start executing b.py and put a in sys.modules and executing a.py. Next it hits from b import d again and find the b in sys.modules but it's empty, throws error: no attribute d.
PS1: the referred post is wrong. import ... as ... is good for circular import.
PS2: from a import c is the same to import a.c in python 3