2017-01-06 5 views
2

Я столкнулся с поведением Python, которое мне кажется очень странным, и я хотел бы это понять.Применение `id` к методу всегда отличается в IPython. Может ли кто-нибудь объяснить это странное поведение?

Обычно я ожидаю, что функция id всегда возвращает одно и то же значение, когда я передаю ему тот же объект. В CPython это соответствует расположению объекта в памяти.

Когда я создаю объект и применяю id, результат всегда один и тот же, но когда я использую id по связанному методу объекта, результат изменяется. Почему это? Является ли новый метод создаваемым каждый раз, когда я получаю атрибут метода?

Я впервые заметил это в IPython, но было сложнее создать скрипт, который показывает то же поведение. Может быть, это частично IPython?

Мне удалось написать небольшой блок, который частично воссоздает поведение.

# Create an object 

class Foo(object): 
    def bar(self): 
     pass 

obj = Foo() 

for _ in range(10): 
    print(id(obj)) 
# ... prints the same number 

for _ in range(10): 
    print(id(obj.bar)) 
# ... in this case the first number is different and the rest are the same 

Это немного отличается, чем просто вставить строку print(id(obj.bar)) в IPython кучу раз, потому что возвращаемые идентификаторы в основном последовательны. Однако, когда я просто запускаю вышеуказанный код как скрипт python, все числа одинаковы, поэтому кажется, что это причуда IPython. Наверное, сейчас вопрос: почему?

+0

Не удается воспроизвести я у IPython (5.1.0) и версию питона (2.7.6). Какую версию Python вы используете и какую версию IPython? –

+0

@JayAtkinson интересный. I * am * способен воспроизводить с помощью ipython 5.1.0 с Python 2.7.12 – elethan

+0

Не отмечать как дублирующее, но, поскольку я не уверен, что они одинаковы, но, конечно же, связаны: http://stackoverflow.com/q/26201305/3642398 – elethan

ответ

1

Связанный метод не является таким же, как несвязанными функции класса:

In [539]: Foo.bar 
Out[539]: <function __main__.Foo.bar> 
In [540]: id(Foo.bar) 
Out[540]: 2951600788 
In [541]: obj=Foo() 
In [542]: obj.bar 
Out[542]: <bound method Foo.bar of <__main__.Foo object at 0xaf63c0cc>> 
In [543]: id(obj.bar) 
Out[543]: 2942557836 
In [544]: obj1=Foo() 
In [545]: id(obj1.bar) # different obj, different bound method 
Out[545]: 2996305612 
In [546]: id(obj.bar) # different from the previous time 
Out[546]: 2942663116 

Так что создает новый связанный метод каждый раз, когда вы ссылаться на него.

Все связанные методы ссылаются на тот же несвязанного метод, Foo.bar:

In [549]: obj.bar.__func__ 
Out[549]: <function __main__.Foo.bar> 
In [550]: id(obj.bar.__func__) 
Out[550]: 2951600788 
In [551]: id(obj1.bar.__func__) 
Out[551]: 2951600788 
0

Это потому, что python создает объекты во время выполнения. Когда вы запускаете свой скрипт, объект создается один раз, и вы можете видеть одинаковое число в каждой итерации цикла. При использовании этой печати в отдельных записях в IPython создаются новые объекты.

4

Каждый раз, когда вы извлекаете метод из экземпляра класса, вы получаете связанный метод , который будет заполнять экземпляр в качестве первого параметра (self) при вызове. Новый метод привязки создается каждый раз. Однако в вашем тесте одновременно существует только один связанный метод; предыдущий становится подходящим для сбора мусора, прежде чем вы создадите следующий. Поэтому, вероятно, (но отнюдь не гарантировано), что новый метод привязки будет выделен по тому же адресу, что и тот, который только что был освобожден, и, следовательно, будет иметь тот же идентификатор. Если вы собрали их всех в списке, чтобы все они существовали одновременно, у них определенно были бы определенные идентификаторы.