Why does the 'is' operator say these metho

2020-04-02 08:49发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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.



回答5:

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'


回答6:

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.