Подтвердить что ты не робот

Почему метод не идентичен самому себе?

Документация Python относительно оператора is говорит:

Операторы is и is not проверяют Идентификатор объекта: x is y является истинным, если и только если x и y являются одним и тем же объектом. x is not y дает обратную истину значение.

Попробуйте следующее:

>>> def m():
...   pass
... 
>>> m is m
True

Документация Python также говорит:

Благодаря автоматической сборке мусора, бесплатные списки и динамический характер дескрипторы, вы можете заметить, казалось бы, необычное поведение при определенных оператор is, как и те, которые связаны с сравнения между методами экземпляра, или констант. Проверьте их для дополнительной информации.

>>> class C:
...   def m():
...     pass
... 
>>> C.m is C.m
False

Я искал больше объяснений, но я не смог их найти.

Почему C.m is C.m false?

Я использую Python 2.x. Как отмечено в ответах ниже, в Python 3.x C.m is C.m истинно.

4b9b3361

Ответ 1

Когда вы запрашиваете атрибут экземпляра, который является функцией, вы получаете связанный метод: вызываемый объект, который обертывает функцию, определенную в классе, и передает экземпляр в качестве первого аргумента. В Python 2.x, когда вы запрашиваете атрибут класса, который является функцией, вы получаете аналогичный прокси-объект, называемый несвязанным методом:

>>> class A: m = lambda: None
...
>>> A.m
<unbound method A.<lambda>>

Этот специальный объект создается, когда вы его запрашиваете, и, по-видимому, не кэшируется нигде. Это означает, что когда вы делаете

>>> A.m is A.m
False

вы создаете два разных объекта несвязанных методов и проверяете их для идентификации.

Обратите внимание, что

>>> x = A.m
>>> x is x
True

и

>>> A.m.im_func is A.m.im_func
True

работает нормально. (im_func - это исходная функция, которую обтекает объект unbound method.)

В Python 3.x, кстати, C.m is C.m имеет значение True, поскольку объекты (прокси-объекты) с незащищенными объектами были полностью удалены, и вы просто получили исходную функцию, которую вы определили.


Это всего лишь один пример очень динамичного характера поиска атрибутов в Python: когда вы запрашиваете атрибут объекта, можно запустить произвольный Python для вычисления значения этого атрибута. Вот еще один пример, когда ваш тест терпит неудачу, в котором гораздо понятнее:

>>> class ChangingAttribute(object):
...     @property
...     def n(self):
...             self._n += 1
...             return self._n
...
...     def __init__(self):
...             self._n = 0
...
>>> foo = ChangingAttribute()
>>> foo.n
1
>>> foo.n
2
>>> foo.n
3
>>> foo.n is foo.n
False
>>> foo.n
6

Ответ 2

Я предполагаю, что вы используете Python 2? В Python 3, C.m is C.m (но C().m is C().m все еще ложно). Если вы введете только C.m в REPL, я уверен, вы видите что-то вроде <UnboundMethod... >. Обертка UnboundMethod делает очень мало, кроме проверки isinstance(self, cls). (Кажется довольно бессмысленным создание обертки для этого? Это так, поэтому он был удален в Python 3 - C.m - это просто функция). Новый экземпляр экземпляра создается по запросу при каждом обращении к методу - C.m создает один, другой C.m создает другой. Поскольку это разные экземпляры, C.m is not C.m.

Близко связаны связанные методы, которые позволяют вам делать f = obj.method; f(*args), но также вызывать instance.method is not instance.method. При инициализации все функции, определенные в классе (читайте: все методы, за исключением, конечно, обезьян.) Становятся свойствами экземпляра. Когда вы обращаетесь к ним, вместо этого вы получаете свежий экземпляр обертки (связанный метод) вокруг простой функции. Эта оболочка запоминает экземпляр (self), а при вызове с (arg1, arg2, ..., argN) просто передает эти функции функции - с self, добавленным в качестве первого аргумента. Обычно вы не замечаете, потому что сразу вызываете метод, но это то, что позволяет пропускать self неявно, не прибегая к обману уровня языка.

Подробнее см. историю Python, а также историю.

Ответ 3

Поскольку C.m() не является статическим методом класса C:

Попробуйте следующее:

class C:
    @staticmethod
    def m():
        pass

print C.m is C.m
# True

c = C()
print c.m is C.m
# True

Поскольку статические методы похожи на переменные класса, нам нужна только одна ссылка для них, поэтому, если мы изменим их связанное значение, это изменение должно быть автоматическим во всех классах и экземплярах этого класса.

С другой стороны, в вашем примере C.m не является статическим методом, поэтому Python делает предположение, что его следует рассматривать как нестатический метод, поэтому всякий раз, когда вы вызываете C.m, он возвращает новый пример:

class C:
   def m():
      pass

a = C.m
b = C.m

print id(a), id(b)
# 43811616, 43355984
print a is b
# False

N.B: статические методы не похожи на методы класса!