classification
Title: Should we define complex.__complex__ and bytes.__bytes__?
Type: Stage: resolved
Components: Versions: Python 3.11
process
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: corona10, ethan smith, gvanrossum, gyu-don, mark.dickinson, serhiy.storchaka, terry.reedy
Priority: normal Keywords: patch

Created on 2015-05-18 21:37 by gvanrossum, last changed 2021-08-26 08:47 by mark.dickinson. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 27887 merged mark.dickinson, 2021-08-22 12:23
PR 27901 merged corona10, 2021-08-23 01:41
PR 27902 merged mark.dickinson, 2021-08-23 10:35
Messages (18)
msg243538 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2015-05-18 21:37
The special methods __complex__ and __bytes__ are not present on the corresponding builtin types.  Compare this to __int__ and __float__, which do exist on int and float, respectively.  Should we add the eponymous methods to complex and bytes?

(This came up in the context of PEP 484: https://github.com/ambv/typehinting/issues/68#issuecomment-88130156 )
msg243842 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2015-05-22 18:01
To my understanding, the presence of int.__int__ and float.__float__ are implementation issues.  I presume that float(ob) just calls ob.__float__ without slowing down for an isinstance(ob, float) check.  Ditto for int(ob).  The processing for complex(args) and bytes(args) are more complex and currently neither call an eponyous method. Would either be improved if it did?

One difference between int and complex, for instance, that might account for the internal implementation difference is that the second argument of int can only be used with a string first argument, while the second argument of complex cannot be used with a string first argument, and must support multiplication by 1j.  So int.__int__(self) does not allow a second parameter and can (and does) just return self.
msg314623 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-03-29 00:03
The difference between __complex__ and __bytes__ on one side, and __int__ and __float__ on other side is that the latter have dedicated type slots while the former are just entries in the type's dict. Thus testing and calling __int__ and __float__ is much faster.
msg314630 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2018-03-29 01:58
It's not necessary for complex() and bytes() to call the special methods if the argument's type is exactly complex or bytes (respectively) -- these cases are already taken care of by the current code.  But adding these special methods enables other code to be more regular, without having to test for the special cases.
msg389805 - (view) Author: Takumi Kato (gyu-don) Date: 2021-03-30 08:37
Recently, the situation has changed. We should consider this issue again.

typing.SupportsComplex is an ABC with one abstract method __complex__.
Thus, isinstance(complex, typing.SupportsComplex) is False.
typing.SupportsBytes also.

It is nonsense.
msg395471 - (view) Author: Ethan Smith (ethan smith) * Date: 2021-06-09 21:09
While I don't think it is nonsense, I do think it would be quite useful to add these. I just submitted PRs to typeshed and numpy adding complex to unions that already had SupportsComplex, because of the lack of __complex__. I'd be happy to work on a PR for this if it would be accepted.
msg395474 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-06-09 21:13
Yeah, the more I think about it, the more it looks like we should add the special methods -- even if they won't necessarily be called *if the type is exactly 'complex' or 'bytes'*.

Now, until we've written and released the code we won't know for sure whether this might break somebody's corner case, so we should play it safe and only do this for 3.11 and make sure it's mentioned in the What's New.
msg400067 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-22 11:03
If the goal is to have `isinstance(obj, typing.SupportsComplex)` pass for objects that are convertible to complex, then we'll need `int.__complex__` and `float.__complex__` implementations as well as `complex.__complex__`.
msg400068 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-22 11:16
> [...] we'll need `int.__complex__` and `float.__complex__` implementations as well as `complex.__complex__`.

The real problem here is that the "typing.SupportsComplex" protocol isn't a good match for code that needs to know that a given value `x` can be treated as though it were a complex number.

The test that best matches "usable as a complex number" seems to be that type(x) implements at least one of `__index__`, `__float__` or `__complex__`, or that `x` is a subclass of complex.

It looks to me as though the right thing to do here is to just implement complex.__complex__, but not int.__complex__ or float.__complex__.  Then at least we can remove the subclass test from the above and express the test purely in terms of special methods: __index__, __float__ and __complex__. And then perhaps it's for the typing module to find a more convenient way to express the union of typing.SupportsIndex, typing.SupportsFloat and typing.SupportsComplex.
msg400109 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-08-22 23:02
What about __bytes__?
msg400113 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2021-08-23 00:45
@guido

>>> issubclass(bytes, typing.SupportsBytes)
False

IMHO, supporting is reasonable.
msg400114 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-08-23 01:35
So let’s add that in a separate PR.
-- 
--Guido (mobile)
msg400123 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-08-23 05:54
Defining complex.__complex__ and bytes.__bytes__ would not solve anything, because

>>> issubclass(int, SupportsComplex)
False
>>> issubclass(float, SupportsComplex)
False
>>> issubclass(bytearray, SupportsBytes)
False
>>> issubclass(memoryview, SupportsBytes)
False

If SupportsComplex and SupportsBytes are just for "has __complex__/__bytes__ method", they are virtually useless. If their meaning is "can be converted to complex/bytes", it is different story, and it should be fixed be adding subclasshooks which check existence of alternate methods (__float__, __index__, supporting the buffer protocol).
msg400124 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-23 08:16
New changeset 6082bb5addab93755ab6e2bd2ed6021b391e10d1 by Mark Dickinson in branch 'main':
bpo-24234: implement complex.__complex__ (GH-27887)
https://github.com/python/cpython/commit/6082bb5addab93755ab6e2bd2ed6021b391e10d1
msg400126 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2021-08-23 10:02
New changeset 24b63c695ae0a95b06379eaadace66735abac1e2 by Dong-hee Na in branch 'main':
bpo-24234: Implement bytes.__bytes__ (GH-27901)
https://github.com/python/cpython/commit/24b63c695ae0a95b06379eaadace66735abac1e2
msg400127 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-23 10:05
> If SupportsComplex and SupportsBytes are just for "has __complex__/__bytes__ method", they are virtually useless.

I agree that "SupportsComplex" isn't directly useful in user-land. I think its main value is as a building block in things like `Union[SupportsComplex, SupportsFloat, SupportsIndex]`.

For me, the gain from implementing complex.__complex__ is that the test "can be used as a complex number" can now be expressed purely in terms of the protocols offered, without reference to concrete types.
msg400129 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-23 10:36
We've got some buildbot failures; GH-27902 should fix them. Apologies for not catching this while reviewing GH-27901.
msg400326 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-26 08:47
All done, I think. Closing.
History
Date User Action Args
2021-08-26 08:47:44mark.dickinsonsetstatus: open -> closed

messages: + msg400326
stage: patch review -> resolved
2021-08-23 10:36:45mark.dickinsonsetmessages: + msg400129
2021-08-23 10:35:13mark.dickinsonsetpull_requests: + pull_request26360
2021-08-23 10:05:09mark.dickinsonsetmessages: + msg400127
2021-08-23 10:02:02corona10setmessages: + msg400126
2021-08-23 08:16:16mark.dickinsonsetmessages: + msg400124
2021-08-23 05:54:21serhiy.storchakasetmessages: + msg400123
2021-08-23 01:41:47corona10setpull_requests: + pull_request26359
2021-08-23 01:35:02gvanrossumsetmessages: + msg400114
2021-08-23 00:45:25corona10setmessages: + msg400113
2021-08-22 23:02:02gvanrossumsetmessages: + msg400109
2021-08-22 12:23:00mark.dickinsonsetkeywords: + patch
stage: patch review
pull_requests: + pull_request26341
2021-08-22 11:16:34mark.dickinsonsetmessages: + msg400068
2021-08-22 11:03:45mark.dickinsonsetmessages: + msg400067
2021-08-22 10:35:42corona10setnosy: + corona10
2021-06-09 21:13:52gvanrossumsetmessages: + msg395474
versions: + Python 3.11, - Python 3.5
2021-06-09 21:09:21ethan smithsetnosy: + ethan smith
messages: + msg395471
2021-03-30 08:37:32gyu-donsetnosy: + gyu-don
messages: + msg389805
2018-03-29 15:25:14mark.dickinsonsetnosy: + mark.dickinson
2018-03-29 01:58:59gvanrossumsetmessages: + msg314630
2018-03-29 00:04:41serhiy.storchakalinkissue33055 superseder
2018-03-29 00:03:47serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg314623
2015-05-22 18:01:12terry.reedysetnosy: + terry.reedy
messages: + msg243842
2015-05-18 21:37:35gvanrossumcreate