id of Point objects are different, because they're different objects and there is no cache/interning mechanism for them (which would be wrong because they're mutable).
== works because when invoking == on  Point, you call __eq__ and it's coded like this:
def __eq__(self, other):
     return id(self.x) == id(other.x) and id(self.y) == id(other.y)
so, it's wrong, but it works most of the time because of interning of integers from -5 to 256 in CPython (further tests show that it works with bigger values but it's not guaranteed). Counter example:
a = 912
b = 2345
point = Point(a, b)
print(point == Point(456*2, b))
you'll get False even if 456*2 == 912
Rewrite as this so you won't have surprises with big integers:
def __eq__(self, other):
     return self.x == other.x and self.y == other.y
If you remove this __eq__ method, you'll get False, as in that case, Python default == operator on an unknown object only has object identity to perform comparisons.
But the purpose of == is to compare object contents, not ids. Coding an equality method that tests identities can lead to surprises, as shown above. 
In Python when people use ==, they expect objects to be equal if values are equal. Identities are an implementation detail, just forget about it.
(Previous versions of Python require you do define __ne__ as well, as it's not automatically the inverse of __eq__ and can lead to strange bugs)
In a nutshell: don't use is (besides is None idiom) or id unless you're writing a very complex low-level program with caching and weird stuff or when debugging your program.