Почему оператор "есть" говорит, что эти методы не совпадают?

Рассмотрим этот код:

class Person(object):
   def sayHello(self):
       return 'Hello'

print(Person().sayHello is Person().sayHello)

Я ожидаю, что он покажет True. Почему он показывает False?

Ответ 1

Методы on привязаны к экземплярам в время выполнения. Когда вы запустите следующий код:

print(Person().sayHello is Person().sayHello)

вы создаете два экземпляра и каждый раз, когда у вас есть другой адрес памяти.

>>> Person().sayHello
<bound method Person.sayHello of <__main__.Person object at 0x7fbe90640410>>
>>> Person().sayHello
<bound method Person.sayHello of <__main__.Person object at 0x7fbe90640490>>

Примечание: Все, что у нас есть в Python, - это время выполнения; не существует отдельного времени компиляции.

Ответ 2

Это два разных экземпляра одного класса. Функции sayHello являются связанными методами.

То есть, если у вас есть экземпляр класса:

p = Person()

и вы найдете в нем атрибут:

p.sayHello

тогда Python сначала рассмотрит фактические атрибуты экземпляра, и если он не найдет здесь атрибут, он рассмотрит класс. Если он находит метод класса этого имени, он превращает его в связанный метод, связанный с этим экземпляром. Это волшебство, которое приводит к тому, что экземпляр объекта передается как первый аргумент (self) в sayHello.

Итак, Person().sayHello is Person().sayHello создает два экземпляра, создает два разных метода привязки, основанных на том же методе, который определен в классе, и поэтому is возвращает False, потому что они разные методы.

Ответ 3

Я собираюсь предположить, что вы намеренно сравниваете сами объекты метода, а не то, что вы действительно хотели сравнить выходные строки и просто забыли поставить () после sayHello.

Попробуйте этот эксперимент:

a = Person()
b = Person()

a.sayHello
b.sayHello

Вы увидите, что a.sayHello отображается как нечто вроде

<bound method Person.sayHello of <__main__.Person instance at 0x102cc8ef0>>

... тогда как b.sayHello отображается аналогично, но с другим указателем родительского экземпляра:

<bound method Person.sayHello of <__main__.Person instance at 0x102d31908>>

Связанный метод одного экземпляра a Person сам по себе является другим экземпляром (метода) из связанного метода с тем же именем из другого экземпляра Person. Вы можете подтвердить это с помощью id(a.sayHello) и id(b.sayHello), которые возвращают хэш-идентификаторы двух соответствующих методах привязки - они будут разными. Поскольку ваш код Person().sayHello is Person().sayHello создает два разных экземпляра Person "на лету", ситуация такая же, как и в примерах с именованными экземплярами a и b.

Ответ 4

Это было бы True, если вы вызвали sayHello:

print(Person().sayHello() is Person().sayHello())

В вашем коде вы фактически сравниваете методы с объектами и проверяете, имеют ли они одинаковый идентификатор (которого у них нет). Также обратите внимание на разницу между:

"Hello" is "Hello"

и

"Hello" == "Hello"

Во-первых, вы сравниваете идентичность объектов (что является одним и тем же из-за повторного использования строки, вызовите id("Hello") несколько раз, чтобы увидеть это). Во втором случае вы сравниваете содержимое строк, чтобы убедиться, что они равны (то есть имеют одинаковые символы). Теперь те же строки будут иметь одинаковые идентификаторы, но я не уверен, что это допущение выполняется во всех реализациях Python.

Ответ 6

Если вам нужно выражение, которое возвращает True, вы можете попробовать следующее:

print(Person.sayHello is Person.sayHello) 

Чтобы добавить к путанице, выполните:

>>> 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'

и даже:

>>> say(say)
'Hello'
>>> say(None)
'Hello'
>>> 

Это происходит потому, что:

>>> say
<function Person.sayHello at 0x02AF04B0>

say относится к Person.sayHello, который является функцией, которая может быть вызвана, но которая нуждается в параметре, но в этом конкретном случае параметр не имеет значения.

Теперь, если вы не хотите продолжать поставлять бесполезный параметр, вы можете связать его автоматически:

>>> p=Person()
>>> p.sayHello()
'Hello'