Consider this code:
class Person(object):
def sayHello(self):
return 'Hello'
print(Person().sayHello is Person().sayHello)
I would expect it to show True. Why does it show False?
Consider this code:
class Person(object):
def sayHello(self):
return 'Hello'
print(Person().sayHello is Person().sayHello)
I would expect it to show True. Why does it show False?
Methods on are bound to instances at runtime. When you run the following code:
print(Person().sayHello is Person().sayHello)
you create two instances and each time you have a different memory address.
>>> Person().sayHello
<bound method Person.sayHello of <__main__.Person object at 0x7fbe90640410>>
>>> Person().sayHello
<bound method Person.sayHello of <__main__.Person object at 0x7fbe90640490>>
Note: All we have in Python is runtime; there is no such thing as a separate compile time.
They are two different instances of the same class. The sayHello
functions are bound methods.
That is, if you have a class instance:
p = Person()
and you lookup an attribute on it:
p.sayHello
then Python first looks at the actual attributes of the instance, and if it does not find the attribute there, it looks at the class. If it finds a class method of that name, it turns it into a bound method, bound to this instance. That is the magic that results in the object instance being passed as the first argument (self
) to sayHello
.
So Person().sayHello is Person().sayHello
creates two instances, creates two different bound methods based on the same method defined on the class, and thus is
returns False
because they're different methods.
I'm going to assume you are intentionally comparing the method objects themselves—and not that you really wanted to compare the output strings and just forgot to put ()
after sayHello
.
Try this experiment:
a = Person()
b = Person()
a.sayHello
b.sayHello
You'll see that a.sayHello
displays as something like
<bound method Person.sayHello of <__main__.Person instance at 0x102cc8ef0>>
...whereas b.sayHello
displays similarly but with a different parent instance pointer:
<bound method Person.sayHello of <__main__.Person instance at 0x102d31908>>
The bound method of one instance of a Person
is itself a different instance (of a method) from the bound method of the same name from a different Person
instance. You can confirm this with id(a.sayHello)
and id(b.sayHello)
which return the identity hashes of the two respective bound methods—they'll be different. Since your code Person().sayHello is Person().sayHello
creates two different Person
instances on the fly, the situation is the same as with my named instance examples a
and b
.
It would be True
if you called sayHello
:
print(Person().sayHello() is Person().sayHello())
In your code you're actually comparing the methods on the objects and checking whether they have the same identity (which they don't). Also note the difference between:
"Hello" is "Hello"
and
"Hello" == "Hello"
In the first, you're comparing the identity of the objects (which is the same due to the string being reused, call id("Hello")
multiple times to see that). In the second, you're comparing the contents of the strings to see if they're equal (i.e., have the same characters). Now, the same strings would also have the same identity but I'm not sure whether that assumption holds across all Python implementations.
If you want an expression which returns True
you can try this:
print(Person.sayHello is Person.sayHello)
Just to add to the confusion, execute:
>>> say = Person.sayHello
>>> say()
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
say()
TypeError: sayHello() missing 1 required positional argument: 'self'
and even:
>>> say(say)
'Hello'
>>> say(None)
'Hello'
>>>
This is because:
>>> say
<function Person.sayHello at 0x02AF04B0>
say
refers to Person.sayHello
which is a function, which can be called, but which needs a parameter, but in this particular case the parameter is irrelevant.
Now, if you don't want to keep supplying the useless parameter, you can have one automatically bound:
>>> p=Person()
>>> p.sayHello()
'Hello'
The is operator means both variables point to the same object instead of having the same value. See Stack Overflow question Understanding Python's “is” operator.