Q:

Weird id() behaviour; different ids, yet id(x) == id(y) returns True?

I created a dummy class with two empty methods:
class Foo:
    def bar(self):
        pass
    def baz(self):
        pass

When I create an instance foo of my Foo class, why is id(foo.bar) == id(foo.baz) True even though the ids are obviously different?
>>> foo = Foo()
>>> id(foo.bar)
31344648
>>> id(foo.baz)
35951432
>>> id(foo.bar) == id(foo.baz)
True

However, using a function like this:
def is2(obj1, obj2):
    return id(obj1) == id(obj2)

The two methods' ids are no longer equal:
>>> foo = Foo()
>>> is2(foo.bar, foo.baz)
False

A:

Methods are wrappers that are re-created on demand. Each time you refer to foo.baz a new method object is created.
Next, memory locations can be reused; id() values are only unique for the lifetime of an object. When the object no longer is needed (the reference count drops to 0) Python removes that object and is free to re-use the same memory space.
By doing id(foo.bar) the new method object only lives for the duration of the id() call. The next id(foo.baz) call is then free to reuse that memory location, and your == equality test returns true, purely because Python is being economic with memory allocation.
That the memory locations were not equal when you were testing the methods on separate lines is as much a coincidence that they were equal in the equality test. Neither is a given. Other objects are created in the interpreter at different stages and can end up reusing a memory location before the next method object is created, for example

