1

Well, this is probably the weirdest behaviour I've come accross in Python. Consider the following code:

class wow:
    var = 90

    def __init__(self):


        self.wow = 30

    def great(self):
        self.eme = 90
        print('wow')


var = wow()
varss = wow()
varsss = wow()



print(id(var.great))
print(id(varss.great))
print(id(varsss.great))
print(var.great is varsss.great)

Output:

1861171310144
1861171310080
1861171310144
False

Why, everytime I create a new object from the same class, is there everytime a new function created , and why do the memory addresses of the first one and third one match, but not the second one (nor id(wow.great)). And why when I compare the two with the same memory addresses does it return false?

Also another question, a new object gets created from the wow class, do all the methods of the wow class actually get added to that object, or does the object kind of call the method from the class whenever its needed. I ask this question because I can't see the methods of the wow class when I do var.dict?

Also, when I create the three different instances, will each of those instances actually have its own functions, or will the functions be made availlable by the wow class and all instances can just access the same functions whenever they want so the functions don't need to be copied in memory 3 times?

Lastly, I'd like to know if this is actually the same case for C#, would each instance have its own functions (non static) or will there only be one of the functions that each instance can access?

kenshieva
  • 101
  • 6
  • Just as a sidenote, `vars` is a built-in function so it's better not to use it as a variable name. – The Thonnu Aug 24 '22 at 13:29
  • 2
    I can't reproduce your first question about the id's, they are all the same. – Kraigolas Aug 24 '22 at 13:29
  • This is also 4 complete questions in 1; this question needs more focus. – Kraigolas Aug 24 '22 at 13:31
  • In Python a function is a complete object, in C# they are not (there are implicit ways of creating function like objects such as `Action`, but those are a separate thing) – UnholySheep Aug 24 '22 at 13:35
  • Does this answer your question? [Does Python really create all bound method for every new instance?](https://stackoverflow.com/questions/30378912/does-python-really-create-all-bound-method-for-every-new-instance) – SargeATM Aug 24 '22 at 13:42

1 Answers1

0

The post linked by SargeATM in the comments (link) actually explains a lot about what is going on. Be sure to check it out ;). Every time you access a method on an instance, a bound method object is created. You can think of this as a separate object which works roughly in the following way:

class BoundMethod:

    def __init__(self, instance, method):
        self.instance = instance
        self.method = method

    def __call__(self, *args, **kwargs):
        return self.method(self.instance, *args, **kwargs)

So, everytime you access var.great, varss.great, or varsss.great, a new bound method object is generated.

There is no clear for the memory addresses you are observing. When you execute print(id(var.great)), a bound method object is created with reference count equal to 1, its id is printed, and the reference count is decreased. The bound method object will then be de-allocated. The behaviour you are observing is most likely a combination of the python memory management implementation, and possibly some randomness. Personally, when I run your code, all memory addresses are equal -- which means that the same memory gets used everyime.

However, when you execute print(var.great is varsss.great), two separate bound method objects are created, with different memory addresses (because obviously, the same memory cannot be used now). To observe this more clearly, let's rewrite the code:

>>> x = var.great
>>> y = varsss.great
>>> print(id(x))
3018906862144
>>> print(id(y))
3018906862272
>>> print(x is y)
False

So, print(var.great is varsss.great) does not compare objects with the same memory address. The only reason it looked like that is that in your preceding print calls, the same memory happened to be used when allocating the bound method object.


Methods are stored on the class object. Method calls in Python, such as var.great(), are essentially equivalent to type(var).great(var), although this is achieved by first creating the bound method object, which would roughly be equivalent to bound_method = BoundMethod(var, type(var).great).

You can also assign methods/functions as attribute to instances directly, but then they do not behave as normal methods (i.e. no bound method object is created. Hence, the function will not be called with self as an argument). This behaviour can be observed using the following code:

class X:
    def __init__(self):
        self.amazing = lambda *args: args 

x = X()
print(x.amazing())

(the output of this code is ())

kolokkol
  • 148
  • 1
  • 2
  • 9