a == b ложно, но id(a) == id(b) верно?

Столкнулся со следующим:

>>> class A:
...     def __str__(self):
...             return "some A()"
... 
>>> class B(A):
...     def __str__(self):
...             return "some B()"
... 
>>> print A()
some A()
>>> print B()
some B()
>>> A.__str__ == B.__str__
False # seems reasonable, since each method is an object
>>> id(A.__str__)==id(B.__str__)
True # what?!

Что тут происходит?


person bb.    schedule 23.02.2010    source источник


Ответы (3)


Когда оценивается строка id(A.__str__) == id(B.__str__), создается A.__str__, берется ее идентификатор, а затем выполняется сборка мусора. Затем создается B.__str__, и он оказывается по тому же адресу, что и A.__str__ ранее, поэтому он получает (в CPython) тот же идентификатор.

Попробуйте присвоить A.__str__ и B.__str__ временным переменным, и вы увидите что-то другое:

>>> f = A.__str__
>>> g = B.__str__
>>> id(f) == id(g)
False

Для более простого примера этого явления попробуйте:

>>> id(float('3.0')) == id(float('4.0'))
True
person Mark Dickinson    schedule 23.02.2010
comment
Но тогда почему ››› f = A.__str__ ››› id(f) == id(A.__str__) False - person Krab; 23.02.2010
comment
A.__str__ создан??? Не уверен в этом: это должно быть частью метакласса, который порождает все классы, то есть их базовую ДНК. - person jldupont; 23.02.2010
comment
@jldupont: Python создает несвязанные методы A.__str__ и B.__str__ во время выполнения. users.rcn.com/python/download/Descriptor.htm — хороший ссылка на лежащие в основе механизмы. - person Mark Dickinson; 23.02.2010
comment
@Krab: у вас есть две копии A.__str__, существующие одновременно (очевидно, Python никоим образом не кэширует ранее созданные несвязанные методы). Любые два разных объекта, которые существуют одновременно, должны иметь разные идентификаторы. - person Mark Dickinson; 23.02.2010
comment
хм... узнавать что-то новое о Python каждый день! Спасибо! Не уверен, каковы последствия такого поведения, когда дело доходит до использования id для хеширования объектов.... - person jldupont; 23.02.2010
comment
хм... неужели они так быстро собирают мусор? Я все еще не убежден в этом объяснении. - person jldupont; 23.02.2010
comment
@jldupont, CPython пересчитывается, поэтому большинство вещей сразу же собирается мусором. Это правильное объяснение того, что происходит. - person Mike Graham; 23.02.2010
comment
@jldupont: Проверить теорию легко! Попробуйте создать подкласс myfloat для float и переопределить __new__ и __del__, чтобы они правильно регистрировали свои вызовы. Теперь посмотрите последовательность операций при оценке id(myfloat(1.0)) == id(myfloat(2.0)). (Хотя вызов __del__ не обязательно напрямую соответствует сборке мусора.) - person Mark Dickinson; 23.02.2010
comment
@jldupont, кстати, вы обычно не используете id для самостоятельного определения хеша. Он используется по умолчанию для экземпляров классов, которые вы создаете, что является надежным и не имеет тонкости дескриптора. Для метода (который создается «на лету») хэш зависит от базовой функции (которая является постоянной); нечасто бывают случаи, когда это затруднило бы выбор подходящей основы для хэша. - person Mike Graham; 23.02.2010
comment
@Mike Graham: проверил поведение сборки мусора, как намекнули: теперь я убежден. Спасибо. - person jldupont; 23.02.2010
comment
+1: за занимательную дискуссию и указание на новый учебный материал. Ваше здоровье. - person jldupont; 23.02.2010

Следующие работы:

>>> id(A.__str__.im_func) == id(A.__str__.im_func)
True
>>> id(B.__str__.im_func) == id(A.__str__.im_func)
False
person honzas    schedule 23.02.2010

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

class A:
    def __str__(self):
        return "some A()"

    def strWasOverridden(self):
        return A.__str__ != self.__str__
person Sjuul Janssen    schedule 08.05.2014
comment
Вообще-то, нет. Это всегда так, потому что экземпляр метода никогда не равен методу класса. - person Fred Foo; 21.10.2014