classification
Title: equivalent functools.partial instances should compare equal
Type: enhancement Stage: patch review
Components: Library (Lib) Versions: Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Jelle Zijlstra, abarry, eryksun, r.david.murray, rhettinger, serhiy.storchaka, shakur shams Mullick, theller, vstinner
Priority: normal Keywords: easy, patch

Created on 2014-04-02 07:57 by theller, last changed 2016-06-07 16:08 by shakur shams Mullick.

Files
File name Uploaded Description Edit
issue21130.patch shakur shams Mullick, 2016-02-12 14:18
partial_richcompare_1_regenerated.patch abarry, 2016-06-04 19:53 review
partial_richcompare_2.patch abarry, 2016-06-04 21:25 review
issue_21130_2.patch shakur shams Mullick, 2016-06-07 16:08 review
Messages (17)
msg215363 - (view) Author: Thomas Heller (theller) * (Python committer) Date: 2014-04-02 07:57
I think that 'equivalent' functools.partial objects should compare equal, so the following snippet SHOULD print True:

>>> import functools
>>> f = lambda x: x
>>> a = functools.partial(f, 42)
>>> b = functools.partial(f, 42)
>>> a == b
False
>>>
msg215369 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2014-04-02 09:56
Even equivalent functions don't compare as equal:

   >>> (lambda x: x*x) == (lambda x: x*x)
   False
msg215374 - (view) Author: Thomas Heller (theller) * (Python committer) Date: 2014-04-02 10:39
Sure.  I'm not sure 'equivalent' is the word I should have used.

I suggest (and I would have expected) that partial instances (a, b) should compare equal when

  a.func == b.func \
  and a.args == b.args \
  and a.keywords == b.keywords
msg215377 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2014-04-02 12:11
Function equality is based on identity, as inherited from object. But there's a precedent for this idea in method_richcompare, which compares the method's __func__ and __self__: 

http://hg.python.org/cpython/file/04f714765c13/Objects/classobject.c#l208
msg215403 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-04-02 21:19
But since the two partial instances are (conceptually, I don't care how they are implemented) two separate functions, then reasoning by analogy from two identical functions not comparing equal, I would expect two partial instances to not compare equal.
msg215405 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-04-02 21:20
Of course, as soon as I hit send, I had second thoughts :).  I guess a partial is a binding, not a function.
msg215510 - (view) Author: Thomas Heller (theller) * (Python committer) Date: 2014-04-04 11:33
My usecase is: I create kind of bound methods with functools.partial.

Apologies for the confusion by using the word 'equivalent'; what I mean is that partial instances should (IMO) compare equal when they contain the same function and args/keywords which compare equal.

I guess functools.partialmethod has the same problem and I would suggest the same fix/enhancement but I have not used them yet because I have to write python2/3 compatible code.

Anyway; if this behaviour is not seen as a bug then it is probably python-ideas material, but I'm too tired to start a discussion there atm.
msg215650 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2014-04-06 06:48
> I guess a partial is a binding, not a function.

Viewed in that light, it makes sense to implement same logic as used for bound methods in method_richcompare.
msg260184 - (view) Author: shakur shams Mullick (shakur shams Mullick) * Date: 2016-02-12 14:21
I have submitted a patch for the issue. Please review and give feedback.
msg267300 - (view) Author: Jelle Zijlstra (Jelle Zijlstra) * (Python triager) Date: 2016-06-04 19:29
I think the patch needs more tests, including:
- Comparing a partial against some other objects
- Comparing two partials with a different function
- Comparing two partials with different args

You also need to check each call to PyObject_RichCompareBool for error (it returns -1 on error). I believe the current implementation would silently ignore the error. This behavior should also be tested.

Rietveld is not picking up this diff, maybe because it is a git rather than an hg diff. Generating the diff with hg will make it easier to review.
msg267307 - (view) Author: Anilyka Barry (abarry) * (Python triager) Date: 2016-06-04 19:53
Git diffs work, but this one doesn't apply cleanly. I manually fixed it and re-generated the patch. Rietveld should pick it up now.

Otherwise, what Jelle said. I'm not sure if the macro has the best name though - I'd probably name it `PyPartial_CheckExact` and reserve `PyPartial_Check` to allow checking for subclasses. Not sure if we need to add both in here, but it would be weird to have only one (even if the other is unused).

(Also, depending which of this one or #27137 gets committed first, the other patch will need to be updated. I'll probably do it since the other is mine.)
msg267321 - (view) Author: Anilyka Barry (abarry) * (Python triager) Date: 2016-06-04 21:25
New patch with Jelle's suggestions. I completely removed the macros since we need to (potentially) call Python code with PyObject_IsInstance, and need to check for return code.

I also reverted some of the unrelated changes.
msg267421 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-06-05 16:07
I support Jelle's comments and have added other comments.
msg267422 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-06-05 16:10
There is a question: should partial(func) be equal to func?
msg267437 - (view) Author: shakur shams Mullick (shakur shams Mullick) * Date: 2016-06-05 17:14
Emanuel Barry if you want to take it over, I will stop and will not modify my patch further. Otherwise please let me know.
msg267441 - (view) Author: Anilyka Barry (abarry) * (Python triager) Date: 2016-06-05 17:40
Shakur - Feel free to take over and work on it.

Serhiy - I don't think that partial(func) == func should be, in the same sense that (0, 1, 2), [0, 1, 2] and range(3) aren't equal even though they fundamentally represent the same thing.
msg267700 - (view) Author: shakur shams Mullick (shakur shams Mullick) * Date: 2016-06-07 16:08
Submitted a new patch addressing the review comments.
History
Date User Action Args
2016-06-07 16:08:41shakur shams Mullicksetfiles: + issue_21130_2.patch

messages: + msg267700
2016-06-05 17:40:46abarrysetmessages: + msg267441
2016-06-05 17:14:13shakur shams Mullicksetmessages: + msg267437
2016-06-05 16:10:31serhiy.storchakasetmessages: + msg267422
2016-06-05 16:07:17serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg267421
2016-06-04 21:25:22abarrysetfiles: + partial_richcompare_2.patch

messages: + msg267321
2016-06-04 19:53:49abarrysetfiles: + partial_richcompare_1_regenerated.patch
versions: + Python 3.6, - Python 3.5
nosy: + abarry

messages: + msg267307

stage: needs patch -> patch review
2016-06-04 19:29:00Jelle Zijlstrasetnosy: + Jelle Zijlstra
messages: + msg267300
2016-03-16 23:47:08vstinnersetnosy: + vstinner
2016-02-12 14:21:06shakur shams Mullicksetnosy: + shakur shams Mullick
messages: + msg260184
2016-02-12 14:18:59shakur shams Mullicksetfiles: + issue21130.patch
keywords: + patch
2014-04-06 06:48:08rhettingersetkeywords: + easy

messages: + msg215650
stage: test needed -> needs patch
2014-04-04 21:39:04terry.reedysetstage: test needed
type: behavior -> enhancement
versions: + Python 3.5
2014-04-04 11:33:12thellersetmessages: + msg215510
2014-04-02 21:20:49r.david.murraysetmessages: + msg215405
2014-04-02 21:19:24r.david.murraysetnosy: + r.david.murray
messages: + msg215403
2014-04-02 20:51:27rhettingersetmessages: - msg215397
2014-04-02 20:19:09rhettingersetmessages: + msg215397
2014-04-02 12:11:23eryksunsetnosy: + eryksun
messages: + msg215377
2014-04-02 10:39:27thellersetmessages: + msg215374
2014-04-02 09:56:54rhettingersetnosy: + rhettinger
messages: + msg215369
2014-04-02 07:57:52thellercreate