0

Say I have a class:

class Thing:
    i = 0
    def __init__(self, a):
        self.a = a
    def doStuff(self):
        print(self.a)
    @classmethod
    def doClassThings(cls):
        cls.i += 1

Then I create a couple of objects from this class:

obj1 = Thing(2)
obj2 = Thing(4)

How many instances of the "doStuff" and "init" methods are there in the program's memory at this time? Are there separate but identical copies of these methods for each of the two objects? Or is there only one "doStuff" method which is shared by both of the thing instances?

For class methods, my understanding is that there is only one instance of each classmethod, and it can be called by writing

Thing.doClassThings()

And that there is also only one "i" variable, shared by the whole class. Is this correct?

Either way, as far as the instance methods go, I don't know how Python works as far as memory use. I really, really don't like the idea of there being copies of these instance methods, because they are identical and this is redundant.

Finally, if Python does create multiple copies of each instance function for every instance of the class, how can I write code that doesn't do this?

  • When you resolve `obj1.doStuff`, a new `bound method` object is created for the lifetime of that method reference. This is just a light wrapper binding the instance to the `self` parameter. You can read some more on the internals involved here: https://stackoverflow.com/questions/13348031/python-bound-and-unbound-method-object – Patrick Haugh Jun 28 '18 at 15:10

1 Answers1

0

Note that I don't know the specifications for this and the following are just my observions and predictions.

How many instances of the "doStuff" and "init" methods are there in the program's memory at this time? Are there separate but identical copies of these methods for each of the two objects? Or is there only one "doStuff" method which is shared by both of the thing instances?

If you print doStuff and __init__ you will see that they have different memory addresses: E.g. print(ob1.doStuff) (don't put parentheses after because we don't want to call the function) gives me <bound method Thing.doStuff of <__main__.Thing object at 0x0000026FE45C9128>>, but print(obj.doStuff) prints <bound method Thing.doStuff of <__main__.Thing object at 0x0000026FE45C9160>>; the two functions have different memory addresses. So indeed, there are two separate but identical copies of the methods.

For class methods, my understanding is that there is only one instance of each classmethod, and it can be called by writing Thing.doClassThings()

This should be correct (printing the functions doesn't give me the address, but comparing the two functions using == returns True.). Although you can also call the function from the object itself (e.g. obj.doClassThings()).

And that there is also only one "i" variable, shared by the whole class. Is this correct?

This seems to be a bit weird:

print(obj1.i, obj2.i, Thing.i)  # prints 0 0 0, as expected
Thing.doClassThings()
print(obj1.i, obj2.i, Thing.i)  # prints 1 1 1
obj1.i += 1
print(obj1.i, obj2.i, Thing.i)  # prints 2 1 1
Thing.doClassThings()
print(obj1.i, obj2.i, Thing.i)  # prints 2 2 2

Like as soon as you change i from an object, it creates its own i, not connected to the rest.

henriman
  • 155
  • 1
  • 11
  • Thanks for showing me how to find the answer. This is more complex than I thought, but I think I am beginning to understand. Essentially, it seems that each time any function is called, or at least for instance methods, a method object is created to resolve that call, and the method object is garbage collected after it is no longer needed. It seems that, if calling a function many times, it may be better to store the reference to that bound method object and use it to call the function you need... – Jacob Wharton Jun 30 '18 at 20:23