When a class definition is encountered Python will execute the body without second thought.
First, it will execute class one, while executing the body (and before actually creating the class), it will encounter class two so it will execute that.
Inside class two it will see a reference to one which still does not exist, hence a NameError will be raised.
One simple way to see the difference in the way Python treats classes/functions is by wrapping the second class definition inside a function. Since Python only compiles functions and doesn't execute them, class one is going to get created:
class one:
x = 1
def wrap():
class two:
y = one.x
one.two = two
Now one exists as a class. If you execute one.wrap the second class definition is going to get executed, the one class is going to be found and then, just to follow along with your original example; I set the class two as an attribute of one to get the same effect you were going for.
As a result, name resolution works fine and class two now has an attribute of class one:
one.wrap()
one.two.y
Out[22]: 1
Do Note: Moving the second class outside of the first class definition works too, but that's besides the point.