5

I would like to know how Python 3 (not 2, please :P) address the following situation:

I have a class and two instances:

class MyClass:
   def something():
      pass

a = MyClass()
b = MyClass()

Do a.something and b.something share the same memory address, or each one have a something declared? How the resolution of calling a.something works?

When I try to see the methods id, they have the same:

id(a.something), id(b.something) # (4487791304, 4487791304)

But when I use is to compare if it's the same, the result is `False:

id(a.something) is id(b.something) # 

And if I go furthermore, and print the available methods and data attributes of a or b, I can see that both of them have the something declared:

['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'something']

Thank you.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • `id(object()), id(object())` – the `id` of values you don’t have any other references to isn’t helpful; you should be comparing `a.something is b.something`. (Or even `a.something is a.something`.) – Ry- May 28 '18 at 17:43
  • You are making a mistake that is going to make everything even more confusing. **never comparing `int` objects using `is`**. Equal `int` objects *are not guaranteed to be the same object*, so `is` may or may not return `True` for equal `int` objects. So don't do `id(a.something) is id(b.something) #` What you *should* have done is `b.something is a.something`. You will find that bound-method objects are in fact re-created each time you access the attribute from an instance. This is because of he way this is implemented, using the descriptor protocol – juanpa.arrivillaga May 28 '18 at 17:44

3 Answers3

1

Do a.something and b.something share the same memory address, or [does] each one have a something declared?

Neither! \o/

The expression a.something evaluates to a new bound version of MyClass.something each time. That means b doesn’t even need to come into it:

>>> a.something is a.something
False

The reason the ids are the same has nothing to do with methods, either:

>>> id(object()) == id(object())
True  # on CPython

The first object gets created, idd, and discarded before the second one does, so the id gets recycled.

Ry-
  • 218,210
  • 55
  • 464
  • 476
0

This:

id(a.something) is id(b.something)

is False for two reasons:

  • Identical (non-small) integers are not identical when compared with is. The short story here is to almost never use is.
  • But even when a == b this is False:

>>> id(a.something) is id(a.something)
False

This is because functions are descriptor objects; every time you do a.something a new bound method is created every time.


Back to your original question:

The (unbound) method of a and b is the same, and the memory location is, too, which you can see because id(a.something) == id(b.something).

L3viathan
  • 26,748
  • 2
  • 58
  • 81
0

Does different instances share the same methods declared in the class?

Yes and No.

If you access to the attributes in different byte-compiled codes you'll always get a different object:

In [12]: id(a.something)
Out[12]: 140252085300232

In [13]: id(b.something)
Out[13]: 140252110215816

And this is completely understandable because when you create different objects the default expectation should be that these objects even if they are instances from same object should not affect each other's functionality. This is to say that if they share same method any changes that one of them applies on the method the other one will be affected as well which is completely wrong!!

But otherwise you'll get the same id. And the reason that you're getting same ids when you do the following:

id(a.something), id(b.something) 

Is because when you trying to access to same attribute from different instances in codes that are byte-compiled together Python (CPython implementation) doesn't recreate the method (at least as far as you haven't changed the method). This is actually an internal optimization that prevents the Python interpreter from creating multiple objects when you haven't changed the methods by for instance calling the __set__ descriptor.

You can test this with a function as well (not just in a tuple) which the codes in its body is byte-compiled together:

In [8]: def foo():
   ...:     print(id(a.something))
   ...:     print(id(b.something))
   ...:     

In [9]: 

In [9]: foo()
140252085766536
140252085766536

Also as mentioned in comments, you shouldn't use is operator for checking the equality of integers, just use ==. The reason is that integers smaller than -5 and greater than 256 are not cached in memory (like those within thatn range). This means that numbers with same value has different id in memory. You can check this like following:

In [14]: 256 is 255+1
Out[14]: True

In [15]: 257 is 255+2
Out[15]: False
Mazdak
  • 105,000
  • 18
  • 159
  • 188