+ 8
Why class 1 instance 1's method is not class 1 instance 2's method? 🐍
I thought it would be crystal clear that all instances of the same class would dynamically share their methods. 💎 For example if we had class A, with method f, and if we created instances a and b, a.f(args) would translate into A.f(a, args) and b.f(args) into A.f(b, args), both objects would have used the 1 same method with just different arguments, that's how I've so far understood it, but... 😶 ⏬ Why a.f is not b.f? 🕵 In the code 📋 you can see that a.f is b.f evaluates to False: https://code.sololearn.com/crRFN7NjjV7x/?ref=app
38 Réponses
+ 5
They are equal, yet different objects. a and b reffer to two different created objects, even if they are the same. Try equaling them, 'is' operator checks, whether both variables reffer to one value. a == b, but a is not b.
+ 5
🌟Prometheus 🇸🇬, it makes no difference, it happens with an instance method as well.
The point is that a.f.__func__ is C.f.
So the instance's f is not the same as the class's f, but it just *stores* the class's f!
But when you use id on the instances's f, you get the same id.
My guess would be that __hash__ is just set up in a way to return __func__'s id instead?
+ 4
You've created a class method. Class methods and static methods are different.
A class method takes cls as first parameter while a static method needs no specific parameters.
Theory:
This probably means the class instances used are probably different but the functions point to the same object.
+ 4
Wow... great discussion guys... I came to the same conclusion you guys reached by building this really messy script.
https://code.sololearn.com/ck5hZkVYq7YF/
Based on my review in this script, I agree with the following conclusions stated here already:
- a.f() is a wrapper function on the object `a` that passes a reference of itself when invoking A.f(a).
- Class instance methods implements its __eq__ comparison operators using __hash__() rather than just id().
I suspect the hash is based on the id() of the class instance and the id() of the functions which would be consistent with everyone's observations.
Thanks again for the great discussion.
+ 3
~ swim ~ Somehow I missed that, thanks, your activity to this post was helpful anyways.
+ 3
~ swim ~, in a place like this, irony can be very dangerous. 😏
+ 3
Okay, I have read up a bit about methods. This is what I make of it - and I hope I'm getting it right.
When you access a method, what happens is this: You get a method object that stores a reference to the class function (__func__).
And when you call it, your method call a.f() is translated by the method object into C.f(a).
So basically, the identity of this method object is not really important, because they all do the same.
And what do we know about Python in other cases, like with immutable types?
What looks in the code as two zeros, might actually be only one zero in memory.
Now run this snippet:
class C:
def f(self):
pass
c = C()
d = C()
print(id(c.f), id(d.f))
a = c.f
b = d.f
print(id(a), id(b))
Funnily, in the first print statement, we have the same id. Obviously Python does aforementioned memory trick and lumps both method objects together as one, because they do the same thing anyway.
But the second print gives different ids, because you created the method objects independently.
+ 3
I think the reason for id([]) == id([]) to be True could be about left side list to be destroyde before right side list is even passed to the id function.
This code the strengthen my hypothesis:
https://code.sololearn.com/cH1GrxpM5JGe/?ref=app
+ 3
~ swim ~ In id([]) == id([]) the operands were just integer or what did you mean?
+ 3
This is to strengthen the theory that the lists got destroyed when id function calls terminated:
https://code.sololearn.com/c5cXnaKVKavD/?ref=app
+ 3
Seb TheS
You convinced me too. Thanks for you explanation!
However, I still have a question. If id really refers to a memory location, then in your example, it means that the python'GC works directly after an object is deleted. Else, that memory location can't be reused directly, but after a few moment, right?
+ 3
Théophile Théophile I don't actually know much about when garbage collection does its work. But by:
https://code.sololearn.com/c14KRJbbAbLu/?ref=app
It seems that id refers to the memory location.
+ 2
Object's don't share methods, they share the class. They can ACCESS each other's methods and attributes because they are in the same class. (If it's the same as Java)
Edit: Nvm ignore what I said, I don't understand python.
+ 2
~ swim ~ "how does compiler/interpretor knows which object method is to be called"
By passing the instance to the self parameter.
+ 2
In fact, a.f and b.f are definitely two different functions. f is considered as an attribute.
Looking at the bytecode, we see that 'a.f is b.f' loads f as an attribute ('LOAD_ATTR') of object a / b.
However, doing id(a.f) == id(b.f) will return True. I don't know if it is the good explanation but I think that since you don't decorate or change the code inside f function, a.f and b.f are bound to the same function.
https://code.sololearn.com/cI3nfJBctpQE/?ref=app
+ 2
~ swim ~ The thing I don't understand is why :
- a.f is b.f #False
- id(a.f) == id(b.f) #True
Where 'is' operator should work the same way as comparing ids of a.f and b.f. Have you got an answer? (I probably misunderstood how 'is' operator works)
+ 2
A riddle for you guys - try this:
class C:
def f():
pass
a, b = C(), C()
print(a.f.__func__ is b.f.__func__)
+ 2
Théophile, yeah, id is supposed, at least in Cpython, to just return the address.
It remains a bit strange, right? If a. f and b.f are not identical, which would make sense, since they're the instances 'containers' for C.f, the id should also be different.
That's why I thought the id call was 'redirected' somehow, but it seems you can't redefine what id does.
https://stackoverflow.com/questions/16153501/JUMP_LINK__&&__python__&&__JUMP_LINK-what-is-the-object-method-of-built-in-id
+ 2
That's just like:
A=[1,2]
B=A.copy()
C=A.copy()
B is C => False
+ 2
It is a bit funny, though. The official rules go like this:
If it doesn't matter (immutables), objects may be allowed to fall together into one, while when it does matter (mutables) the instances *have to* be individual.
Now if I run this code...
print(id([]), id([]))
print(id(list()), id(list()))
... in the first example I get two times the same id, although we have two lists here. It even happens when you fill them up.
By explicitly using the constructor ('new'), you get two ids.
Even if it doesn't make a difference, I would have assumed that Python wouldn't be allowed to meld the two lists.