Issue 3500: unbound methods of different classes compare equal (original) (raw)

Created on 2008-08-04 19:13 by exarkun, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (8)
msg70714 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2008-08-04 19:13
If a method is inherited by two different classes, then the unbound method objects which can be retrieved from those classes compare equal to each other. For example: Python 2.6b2+ (trunk:65502M, Aug 4 2008, 15:05:07) [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> class X: ... def y(self): ... pass ... >>> class A(X): ... pass ... >>> class B(X): ... pass ... >>> A.y == B.y True This is bad behavior because A.y and B.y are otherwise distinguishable (for example, they repr differently, they have different values for the `im_class´ attribute, they cannot be used interchangably for invoking the method because they place different type restrictions on the `self´ parameter, etc).
msg70715 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2008-08-04 19:25
Well, I'm not sure. One could also argue that 1 and 1.0 mustn't compare equal because they cannot be used equally in all circumstances (e.g. __index__), they have different repr's, different types, etc. The question is: what kind of use case does it help to have them compare unequal? I can see the utility of having them compare equal: to check whether a method has been overriden or not. (I'm removing 2.4 and 2.5 anyway since the change would break compatibility)
msg70716 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2008-08-04 19:37
The reason I noticed this is that since they compare and hash equal, if you put two such methods into a set, you end up with a set with one method. Currently, this is preventing me from running two test methods because the method itself is defined on a base class and two subclasses which customize several other methods inherit it. I can only run one test at a time. Having them compare unequal means you can't actually trust unbound method comparison, nor using unbound methods as keys in a dictionary. This means some other mapping structure is required if you want to keep around a bunch of methods and arguments to pass to them. It also means that any time you want to check two methods against each other with the goal of eventually calling one or both of them, you need to use something other than `==´. It seems like calling methods is likely to be a more common use-case than anything else.
msg70717 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2008-08-04 20:07
> The reason I noticed this is that since they compare and hash equal, if > you put two such methods into a set, you end up with a set with one > method. Currently, this is preventing me from running two test methods > because the method itself is defined on a base class and two subclasses > which customize several other methods inherit it. I can only run one > test at a time. But you acknowledge they are really the same method attached to different classes, right? The notion of "unbound method" is mostly an implementation detail. The term occurs only 4 times in the whole Python documentation (according to Google). And in py3k they are gone. (*) Moreover, you say you want them to compare unequal because you *explicitly* want the same method called separately for each class it is defined on. Is there anything preventing you to have a set of (class, method) tuples instead? Because it sounds like the logical thing to do in your case. > Having them compare unequal means you can't actually trust unbound > method comparison, nor using unbound methods as keys in a dictionary. "Trust" is a strong word. You can trust the comparison operator if you agree with its semantics, you cannot trust it if you want different semantics. But that doesn't mean it is generally trustworthy or untrustworthy. Really, this is the same as with numbers: 'b' There are probably use cases where the above is annoying. But, conversely, there are probably use cases where a stricter behaviour would be annoying too. > This means some other mapping structure is required if you want to keep > around a bunch of methods and arguments to pass to them. I disagree. The general use case of keeping a bunch of callables with their respective arguments implies storing bound, not unbound, methods. (how often do you feed an unbound method to an addCallback() ?) > It also means > that any time you want to check two methods against each other with the > goal of eventually calling one or both of them, you need to use > something other than `==´. I don't think there are lots of use cases for comparing *unbound* methods. One such use case is checking for redefinition of inherited methods, and the current __eq__ semantics look fine for that. (*) Python 3.0b2+ (py3k, Jul 29 2008, 20:37:34) [GCC 4.3.1 20080626 (prerelease)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> class A: ... def f(): pass ... >>> type(A.f) <class 'function'> >>> a = A() >>> type(a.f) <class 'method'> >>> def g(): pass ... >>> class B: ... g = g ... >>> B.g is g True
msg70718 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2008-08-04 20:10
Apparently Roundup snipped my numbers example :-) Here it is, hoping it will pass through this time : >>> d = {} >>> d[1] = 'a' >>> d[1.0] = 'b' >>> d[1] 'b'
msg70736 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2008-08-05 13:20
> But you acknowledge they are really the same method attached to > different classes, right? The notion of "unbound method" is mostly an > implementation detail. The term occurs only 4 times in the whole Python > documentation (according to Google). And in py3k they are gone. (*) It's the same function attached to two different classes. I don't really buy the "implementation detail" argument - if Guido says it, then I don't have much choice but to accept it, but I'm going to argue about it with anyone else. :) In this case, I'd say it's definitely not an implementation detail because it has major consequences visible to applications. If I get method x from class A and try to call it with an instance of class B, then it's going to break. I have to know about this behavior in order to write a program that works. Py3k may be different, but I'm not going to talk about Py3k here because I'm only interested in the Python 2.x behavior. > Moreover, you say you want them to compare unequal because you > *explicitly* want the same method called separately for each class it is > defined on. Is there anything preventing you to have a set of (class, > method) tuples instead? Because it sounds like the logical thing to do > in your case. I could do that. I probably will, too, since this code needs to work on Python 2.3 through Python 2.5. I still want to make Python 2.6 better, though. It seems to me that an unbound method is already effectively a tuple of (class, function) - it has a reference to both of those. Requiring applications to tie it up with a class again is just redundant. > "Trust" is a strong word. You can trust the comparison operator if you > agree with its semantics, you cannot trust it if you want different > semantics. But that doesn't mean it is generally trustworthy or > untrustworthy. You're right. "trust" was a bad word to use there. > I don't think there are lots of use cases for comparing *unbound* > methods. One such use case is checking for redefinition of inherited > methods, and the current __eq__ semantics look fine for that. This use-case is already satisfied though - if an application wants to see if the function is the same, then the application can compare the im_func attribute of the method objects.
msg70737 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2008-08-05 14:01
Hi, > It's the same function attached to two different classes. I don't > really buy the "implementation detail" argument - if Guido says it, > then I don't have much choice but to accept it, but I'm going to > argue about it with anyone else. :) I understand that :-) > Py3k may be different, but I'm > not going to talk about Py3k here because I'm only interested > in the Python 2.x behavior. Granted, but Python 2.6 (and subsequent 2.x versions) strives to make it easier to port code to 3.x by gradually reducing compatibility issues. Your proposal to change unbound method __eq__ behaviour would, on the contrary, add another incompatibility between 3.x and 2.x. > This use-case is already satisfied though - if an application wants > to see if the function is the same, then the application can compare > the im_func attribute of the method objects. But your use case is already satisfied as well - you just have to use the (class, method) tuple for comparison. In other words, both use cases can be satisfied by circumventing, if needed, the default __eq__ behaviour. If there's no overwhelming reason to change the current default, keeping 2.x and 3.0 compatibility should prevail. Regards Antoine.
msg124477 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2010-12-22 02:39
Since 2.7 has been released and this behaviour could not be changed in a point release even if agreement that it was a good change was reached, and since it is meaningless in 3.x, I'm closing this issue.
History
Date User Action Args
2022-04-11 14:56:37 admin set github: 47750
2010-12-22 02:39:01 r.david.murray set status: open -> closednosy: + r.david.murraymessages: + resolution: rejectedstage: resolved
2008-08-05 14:01:57 pitrou set messages: +
2008-08-05 13:20:25 exarkun set messages: +
2008-08-04 20:10:46 pitrou set messages: +
2008-08-04 20:07:50 pitrou set messages: +
2008-08-04 19:37:27 exarkun set messages: +
2008-08-04 19:25:15 pitrou set nosy: + pitroumessages: + versions: + Python 3.0, - Python 2.5, Python 2.4
2008-08-04 19:13:33 exarkun create