Id()S of Bound and Unbound Method Objects --- Sometimes the Same for Different Objects, Sometimes Different for the Same Object

id()s of bound and unbound method objects --- sometimes the same for different objects, sometimes different for the same object

Whenever you look up a method via instance.name (and in Python 2, class.name), the method object is created a-new. Python uses the descriptor protocol to wrap the function in a method object each time.

So, when you look up id(C.foo), a new method object is created, you retrieve its id (a memory address), then discard the method object again. Then you look up id(cobj.foo), a new method object created that re-uses the now freed memory address and you see the same value. The method is then, again, discarded (garbage collected as the reference count drops to 0).

Next, you stored a reference to the C.foo unbound method in a variable. Now the memory address is not freed (the reference count is 1, instead of 0), and you create a second method instance by looking up cobj.foo which has to use a new memory location. Thus you get two different values.

See the documentation for id():

Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

CPython implementation detail: This is the address of the object in memory.

Emphasis mine.

You can re-create a method using a direct reference to the function via the __dict__ attribute of the class, then calling the __get__ descriptor method:

>>> class C(object):
... def foo(self):
... pass
...
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x1088cc488>
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x1088d6f90>>

Note that in Python 3, the whole unbound / bound method distinction has been dropped; you get a function where before you'd get an unbound method, and a method otherwise, where a method is always bound:

>>> C.foo
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(None, C)
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x10bc65150>>

Furthermore, Python 3.7 adds a new LOAD_METHOD - CALL_METHOD opcode pair that replaces the current LOAD_ATTRIBUTE - CALL_FUNCTION opcode pair precisely to avoid creating a new method object each time. This optimisation transforms the executon path for instance.foo() from type(instance).__dict__['foo'].__get__(instance, type(instance))() with type(instance).__dict__['foo'](instance), so 'manually' passing in the instance directly to the function object.

Why do different methods of same object have the same `id`?

The same memory location is being used by Python for methods a.f and a.g, which are **two objects with non-overlapping lifetimes*, so id returns same identity for both of them. See more detailed explanations below.

From the documentation for the is operator:

The operators is and is not test for object identity: x is y is true
if and only if x and y are the same object.

From the documentation for the is id

Return the “identity” of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this object
during its lifetime. Two objects with non-overlapping lifetimes may
have the same id() value.

Explanations:

Whenever you look up a method via class.name or instance.name, the method object is created a-new. Python uses the descriptor protocol to wrap the function in a method object each time.

So, when you look up id(a.f) or id(a.g), a new method object is created.

  1. When you grubbing id of a.f, a copy of it is created in memory. This memory location is returned by id.
  2. Since there are no references to the newly created method, it reclaimed by the GC (now memory address is available again).
  3. After you getting id of a.g, a copy of it is created at the same memory address, which you retrieve using id again.
  4. You've got truthy id's comparison.

Good luck!

Horrible redraw performance of the DataGridView on one of my two screens

You just need to make a custom class based off of DataGridView so you can enable its DoubleBuffering. That's it!


class CustomDataGridView: DataGridView
{
public CustomDataGridView()
{
DoubleBuffered = true;
}
}

As long as all of my instances of the grid are using this custom version, all is well. If I ever run into a situation caused by this where I'm not able to use the subclass solution (if I don't have the code), I suppose I could try to inject that control onto the form :) (although I'll be more likely to try using reflection to force the DoubleBuffered property on from the outside to once again avoid the dependency).

It is sad that such a trivially simple thing ate up so much of my time...

Note: Making the answer an answer so the question can be marked as answered

The 'is' operator is not working on objects with the same identity

After id(300) is executed, no more references to 300 exist, so the id is freed. When you execute id(6), it gets that same chunk of memory and stores 6 instead. When you do -300 is 6, -300 and 6 are both referenced at the same time, so they won't have the same address anymore.

If you keep references to both -300 and 6, this happens:

>>> a, b = -300, 6
>>> id(a)
some number
>>> id(b)
some different number; 6 is still in the other memory address.

Note: In CPython, numbers from -5 to 256 (I think) are cached, and will always have the same address, so this will not happen.

Weird id() behaviour; different ids, yet id(x) == id(y) returns True?

Methods are wrappers that are re-created on demand. Each time you refer to foo.baz a new method object is created.

Next, memory locations can be reused; id() values are only unique for the lifetime of an object. When the object no longer is needed (the reference count drops to 0) Python removes that object and is free to re-use the same memory space.

By doing id(foo.bar) the new method object only lives for the duration of the id() call. The next id(foo.baz) call is then free to reuse that memory location, and your == equality test returns true, purely because Python is being economic with memory allocation.

That the memory locations were not equal when you were testing the methods on separate lines is as much a coincidence that they were equal in the equality test. Neither is a given. Other objects are created in the interpreter at different stages and can end up reusing a memory location before the next method object is created, for example

id() vs `is` operator. Is it safe to compare `id`s? Does the same `id` mean the same object?

According to the id() documentation, an id is only guaranteed to be unique

  1. for the lifetime of the specific object, and
  2. within a specific interpreter instance

As such, comparing ids is not safe unless you also somehow ensure that both objects whose ids are taken are still alive at the time of comparison (and are associated with the same Python interpreter instance, but you need to really try to make that become false).

Which is exactly what is does -- which makes comparing ids redundant. If you cannot use the is syntax for whatever reason, there's always operator.is_.


Now, whether an object is still alive at the time of comparison is not always obvious (and sometimes is grossly non-obvious):

  • Accessing some attributes (e.g. bound methods of an object) creates a new object each time. So, the result's id may or may not be the same on each attribute access.

    Example:

    >>> class C(object): pass
    >>> c=C()
    >>> c.a=1

    >>> c.a is c.a
    True # same object each time

    >>> c.__init__ is c.__init__
    False # a different object each time

    # The above two are not the only possible cases.
    # An attribute may be implemented to sometimes return the same object
    # and sometimes a different one:
    @property
    def page(self):
    if check_for_new_version():
    self._page=get_new_version()
    return self._page
  • If an object is created as a result of calculating an expression and not saved anywhere, it's immediately discarded,1 and any object created after that can take up its id.

    • This is even true within the same code line. E.g. the result of id(create_foo()) == id(create_bar()) is undefined.

      Example:

      >>> id([])     #the list object is discarded when id() returns
      39733320L
      >>> id([]) #a new, unrelated object is created (and discarded, too)
      39733320L #its id can happen to be the same
      >>> id([[]])
      39733640L #or not
      >>> id([])
      39733640L #you never really know

Due to the above safety requirements when comparing ids, saving an id instead of the object is not very useful because you have to save a reference to the object itself anyway -- to ensure that it stays alive. Neither is there any performance gain: is implementation is as simple as comparing pointers.


Finally, as an internal optimization (and implementation detail, so this may differ between implementations and releases), CPython reuses some often-used simple objects of immutable types. As of this writing, that includes small integers and some strings. So even if you got them from different places, their ids might coincide.

This does not (technically) violate the above id() documentation's uniqueness promises: the reused object stays alive through all the reuses.

This is also not a big deal because whether two variables point to the same object or not is only practical to know if the object is mutable: if two variables point to the same mutable object, mutating one will (unexpectedly) change the other, too. Immutable types don't have that problem, so for them, it doesn't matter if two variables point to two identical objects or to the same one.


1Sometimes, this is called "unnamed expression".

Why do methods of different objects of same class have same id?

Here is what I think is happening:

  1. When you dereference p1.useless_func, a copy of it is created in memory. This memory location is returned by id
  2. Since there are no references to the copy of the method just created, it gets reclaimed by the GC, and the memory address is available again
  3. When you dereference p2.useless_func, a copy of it is created in the same memory address (it was available), which you retrieve using id again.
  4. The second copy is GCd

If you were to run a bunch of other code and check the ids of the instance methods again, I'll bet the ids would be identical to each other, but different from the original run.

Additionally, you might notice that in David Wolver's example, as soon as a lasting reference to the method copy is obtained the ids become different.

To confirm this theory, here is a shell session using Jython (same result with PyPy), which does not utilize CPython's reference counting garbage collection:

Jython 2.5.2 (Debian:hg/91332231a448, Jun 3 2012, 09:02:34) 
[OpenJDK Server VM (Oracle Corporation)] on java1.7.0_21
Type "help", "copyright", "credits" or "license" for more information.
>>> class parent(object):
... def m(self):
... pass
...
>>> p1, p2 = parent(), parent()
>>> id(p1.m) == id(p2.m)
False


Related Topics



Leave a reply



Submit