Use nb_reserved slot of PyNumberMethods to support __complex__() method?

Despite we have complex builtin type - there is no support for them in the PyNumberMethods structure. Then __complex__() dunder should be implemented as ordinary method. That makes complex initialization relatively expensive wrt one for floats (see also gh-109218: Deprecate weird cases in the complex() constructor by serhiy-storchaka · Pull Request #119620 · python/cpython · GitHub).

Simple benchmarks in my branch wrt main
x1 = 1+1j
class c2(complex): ...
x2 = c2()
class c3:
    def __complex__(self):
        return x1
x3 = c3()

With nb_complex:

$ ./python -m timeit -s 'from a import x1' -unsec 'complex(x1)'
500000 loops, best of 5: 582 nsec per loop
$ ./python -m timeit -s 'from a import x2' -unsec 'complex(x2)'
200000 loops, best of 5: 1.74e+03 nsec per loop
$ ./python -m timeit -s 'from a import x3' -unsec 'complex(x3)'
100000 loops, best of 5: 3.09e+03 nsec per loop

With main:

$ ./python -m timeit -s 'from a import x1' -unsec 'complex(x1)'
500000 loops, best of 5: 659 nsec per loop
$ ./python -m timeit -s 'from a import x2' -unsec 'complex(x2)'
100000 loops, best of 5: 3.25e+03 nsec per loop
$ ./python -m timeit -s 'from a import x3' -unsec 'complex(x3)'
50000 loops, best of 5: 4.32e+03 nsec per loop

There is reserved slot (nb_reserved), maybe we should use one to implement nb_complex? (Here is the patch to play with: Add nb_complex slot to PyNumberMethods (was nb_reserved) by skirpichev · Pull Request #2 · skirpichev/cpython · GitHub). Probably, Number Protocol should also include PyNumber_Complex() function. Edit: note, that PyNumber_Check() returns 1 for complex numbers.

This shouldn’t break existing code; DeprecationWarning will be emitted, if extension uses ordinary method to implement __complex__().

If performance argument isn’t impressive, lets see that docs says about PyNumberMethods: “This structure holds pointers to the functions which an object uses to implement the number protocol.” It’s odd that conversion to complex is not a part of the Number Protocol. (C.f. the numeric ABC.)

Is there arguments to keep nb_reserved? E.g. it wasn’t helpful in last case when PyNumberMethods was changed (matrix multiplication).

1 Like