The operator is checks the object identity, not value. In this case you have two separate functions (objects); thus they have a different identity.
And about the following part:
>>> id(a.f) == id(a.g)
True
Since Python creates the objects at run time, the first time Python attempts to get the id of a.f, the a.g hasn't been defined and based on the Python wiki Two objects with non-overlapping lifetimes may have the same id() value.
So in this case objects a.f and a.g that have non-overlapping lifetimes have equal id.
Return the “identity” of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this object
during its lifetime. Two objects with non-overlapping lifetimes may
have the same id() value.
Some extra notes about is operator:
As I said in aforementioned lines the is operator will check the identity of objects and object in Python will be created at run time. But this is not true for some small types like integers and strings, because they are singletons and not Python objects. Hence they will be located right away in memory like C types.
For better demonstration you can see the following examples:
>>> 100 is 10*10
True
>>>
>>> 1000 is 10*100
False
>>>
And for strings:
>>> 'aaaa'*5 is 'a'*20
True
>>> 'aaaa'*50 is 'a'*200
False