why the address of __init__ in this code is similar:
class c:
def __init__(self,color):
print (f"id of self in __init__ on class is {id(self)}")
def test(self):
print("hello")
print (f"id of __init__ on class is {id(__init__)}")
a=c("red")
print(id(a.__init__))
print(id(a.test))
b=c("green")
b.test()
print(id(b.__init__))
print(id(b.test))
The result is:
Output:
id of __init__ on class is 1672033309600
id of self in __init__ on class is 1672033251232
1672028411200
1672028411200
id of self in __init__ on class is 1672033249696
hello
1672028411200
1672028411200
Methods are attributes of the class, not instances.
No I don't believe that methods are attributes of Class
when i type a=c() for creating new object "a", in memory of computer new object space is creating and then attributes of that object will be created and then methods bound to that object.
Well if that were the case, why would they take a self
parameter then?
A method in an instances is a binding. A method in a class is code.
I have this class:
class A():
varA = 5
def methodA(self):
pass
a = A()
I look at the attributes for the class and the instance.
Output:
>>> dir(A)
['__class__', '__delattr__', ..., 'methodA', 'varA']
>>> dir(a)
['__class__', '__delattr__', ... 'methodA', 'varA']
I look at methodA:
Output:
>>> A.methodA
<function A.methodA at 0x000001FA1DC701F0>
>>> a.methodA
<bound method A.methodA of <__main__.A object at 0x000001FA1DC63FA0>>
So it is true that methodA in class A is different that method A in instanceA, but it is also true that the method, the actual code, is an attribute of the class.
Getting the id for a instance method is interesting. The value changes. It would appear that a bound method is constructed each time you get the method attribute.
Output:
>>> id(a.methodA)
1825282503232
>>> id(a.methodA)
1825282503360
>>> id(a.methodA)
1825283705856
These temporary objects get reused at a rapid rate.
Output:
>>> id(b.methodA)
1825279555584
>>> id(b.methodA)
1825282503360 #<- Same as a.methodA above
I wonder why this is?
Notice to This code below:
class C:
def method(self):
print('I am method')
c1 = C()
c2 = C()
print(id(c1.method)) # id returns the address
>>> 140161083662600
print(id(c2.method))
>>> 140161083662600
id of 2 methods of different objects are identical
and i believe the usage of "self" in creating class is to send object address to class and then method execute in place in class not in object and the result will send to caller.
mi Wrote:and i believe the usage of "self" in creating class is to send object address to class and then method execute in place in class not in object and the result will send to caller.
I don't think underlaying object memory address should be used when try to explain how a class work.
In Python object can point to same memory address,this can happens everywhere.
>>> s = 'hello'
>>> d = s
>>> id(s)
179605616
>>> id(d)
179605616
So here both
s
and
d
name tag to same memory address.
It will soon be complicated.
class C:
def method(self, arg):
return f'I am method {arg}'
c1 = C()
c2 = C()
>>> id(c1.method)
179587328
>>> id(c2.method)
179587328
>>>
>>> id(c1.method('taxi'))
179549648
>>> id(c2.method('bus'))
179588656
>>>
>>> c1.method
<bound method C.method of <__main__.C object at 0x000000000AA193A0>>
>>> c2.method
<bound method C.method of <__main__.C object at 0x000000000AA10B50>>
So method without calling point to same memory address.
When call it with argument the memory address will be different.
Last we see a different memory address
I have an idea why the python object returned by obj.method can be different each time you reference obj.method, and why objA.methodA and objectB.methodB can both return the same object: The object returned when you reference an instance method is a temporary object, the Python equivalent of a C stack pointer.
Python allows recursion. This means each time you execute a function (or method) python has to create an object to hold the program counter and all the local variables for the function call. I think that is what you are getting when you call a.__init__ or a.test in your example. Python is creating "function context" object that implements __call__(). This is why id(a.test) may print different values at different times. You are displaying the object ID of a temporary object.
To test my theory I called made this class:
class A():
def method():
pass
Then I made an instance of A and called id(a.method) multiple times:
Output:
>>> a = A()
>>> id(a.method)
2410911802112
>>> id(a.method)
2410910939520
>>> id(a.method)
2410911802624
>>> id(a.method)
2410911802112
>>> id(a.method)
2410911802624
Notice the id values are different, but repeat.
Next I started assigning variables to a.method.
Output:
>>> x = a.method
>>> y = a.method
>>> z = a.method
>>> id(x)
2410910955584
>>> id(y)
2410911802112
>>> id(z)
2410915105600
>>> id(a.method)
2410915105792
>>> id(a.method)
2410915105856
>>> id(a.method)
2410910939520
>>> id(a.method)
2410915105792
The objects referenced by variables x, y and z are all different. I tried several more variables, and these all get different objects. The objects referenced by these variables no longer appear when I call id(a.method) which still returns different but repeating values.
Assigning the result of a.method to a variable increments the object count, so it will not get garbage collected. I think that is why calling "x = a.method" removes the id(x) from the values that appear when I call id(a.method). This is a good indication that a.method is returning a generated object, not an attribute. If a.method was an attribute it's reference count would not go to zero and it would not be garbage collected. If a.method was an attribute it would also return the same object every time. Instead a.method works like a generator property. It creates a new object when referenced.
I wonder how far off this is from what is actually happening.