11

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?

Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • 1
    See this question http://stackoverflow.com/a/133024/1394473 – tom Nov 21 '14 at 20:15
  • @Test Why do you expect your expression to be True? You must be expecting the two subexpressions to be the same object. What leads you to this assertion? – quamrana Nov 21 '14 at 21:20
  • 2
    @quamrana - because `sayHello` was only defined once. That the method is instantiated to a bound method object isn't exactly blindingly obvious. – tdelaney Nov 21 '14 at 21:22
  • @tdelaney I quite agree. It has taken me quite some time of learning python to realise what is going on with methods bound and unbound. – quamrana Nov 21 '14 at 21:29
  • 1
    ***Because `is` is intended for objects, not methods*** – smci Nov 22 '14 at 01:45
  • 4
    Methods are objects. However, when you access a bound function, you don't get a reference to the originally defined function; you get a method object that is created anew every time the function is accessed. See this answer: http://stackoverflow.com/questions/15977808/why-dont-methods-have-reference-equality – Mike Brennan Nov 22 '14 at 02:18

6 Answers6

15

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.

Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • 10
    [And if you have one instance, you'll *still* get `False`, because you get a new method object on every access.](http://stackoverflow.com/questions/15977808/why-dont-methods-have-reference-equality) – user2357112 Nov 22 '14 at 01:35
  • @user2357112 yes ,sure. – Mazdak Nov 22 '14 at 06:08
  • 4
    And there *is* a separate compile time in Python. Name scope is determined at compile time for example. And various literals are turned into constants, and peephole optimisations are applied at compile time. Doesn't apply here but don't claim it doesn't exist. – Martijn Pieters Nov 25 '14 at 08:04
  • yep , but something like `peephole optimisations` are about compile time at all ! actually `All we have in Python` is about objects ! and processes on the scope of interpreter ! – Mazdak Nov 25 '14 at 16:09
5

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.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
  • 2
    Even on the same instance methods are created anew each time you access them. It doesn't matter that there are two efferent instances here. – Martijn Pieters Nov 25 '14 at 07:53
4

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.

jez
  • 14,867
  • 5
  • 37
  • 64
  • 2
    It doesn't matter that there are different instances. Even with just one instance you get two different method objects if you access the method twice. – Martijn Pieters Nov 25 '14 at 07:54
  • Good point. The `getattr` operation implicit in `a.sayHello` is what *creates* a new bound method-instance every time you do it. For me this was masked by the fact that (possibly depending on platform/implementation) you get the same id number twice if you say `id(a.sayHello)` twice. But you get *different* id numbers if you don't let the first method instance go out of scope. So `as1 = a.sayHello; as2 = a.sayHello; print( [id(as1), id(as2) ])` gives me different numbers, whereas `print([ id(a.sayHello),id(a.sayHello) ])` gives me identical numbers. – jez Nov 25 '14 at 19:02
  • `id()` values can be reused if the object has been deleted. Always create a reference to the object first then test. – Martijn Pieters Nov 25 '14 at 19:25
-1

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.

Simeon Visser
  • 118,920
  • 18
  • 185
  • 180
-1

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'
quamrana
  • 37,849
  • 12
  • 53
  • 71
-2

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.

Community
  • 1
  • 1
Andres
  • 4,323
  • 7
  • 39
  • 53