Skip to content

gh-109218: Improve documentation for the complex() constructor #119687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
105 changes: 72 additions & 33 deletions Doc/library/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,11 @@ are always available. They are listed here in alphabetical order.
See also :func:`format` for more information.


.. class:: bool(x=False)
.. class:: bool(object=False, /)

Return a Boolean value, i.e. one of ``True`` or ``False``. *x* is converted
using the standard :ref:`truth testing procedure <truth>`. If *x* is false
Return a Boolean value, i.e. one of ``True`` or ``False``. The argument
is converted using the standard :ref:`truth testing procedure <truth>`.
If the argument is false
or omitted, this returns ``False``; otherwise, it returns ``True``. The
:class:`bool` class is a subclass of :class:`int` (see :ref:`typesnumeric`).
It cannot be subclassed further. Its only instances are ``False`` and
Expand All @@ -153,7 +154,7 @@ are always available. They are listed here in alphabetical order.
.. index:: pair: Boolean; type

.. versionchanged:: 3.7
*x* is now a positional-only parameter.
The parameter is now positional-only.

.. function:: breakpoint(*args, **kws)

Expand Down Expand Up @@ -371,29 +372,64 @@ are always available. They are listed here in alphabetical order.
support for top-level ``await``, ``async for``, and ``async with``.


.. class:: complex(real=0, imag=0)
complex(string)
.. class:: complex(number=0, /)
complex(string, /)
complex(real=0, imag=0)

Return a complex number with the value *real* + *imag*\*1j or convert a string
or number to a complex number. If the first parameter is a string, it will
be interpreted as a complex number and the function must be called without a
second parameter. The second parameter can never be a string. Each argument
may be any numeric type (including complex). If *imag* is omitted, it
defaults to zero and the constructor serves as a numeric conversion like
:class:`int` and :class:`float`. If both arguments are omitted, returns
``0j``.
Convert a string or a number to a complex number or create a complex number
from the real and imaginary parts.

If the argument is a string, it should contain a decimal number, optionally
preceded by a sign, and optionally followed by the ``j`` or ``J`` suffix,
or a pair of decimal numbers, optionally preceded by a sign, separated by
the ``+`` or ``-`` operator, and followed by the ``j`` or ``J`` suffix.
A string representing a NaN (not-a-number), or infinity is acceptable
in place of a decimal number, like in :func:`float`.
The string can optionally be surrounded by whitespaces and the round
parenthesis ``(`` and ``)``, which are ignored.
The string must not contain whitespace between ``+``, ``-``, the ``j`` or
``J`` suffix, and the decimal number. For example, ``complex('1+2j')`` is fine, but
``complex('1 + 2j')`` raises :exc:`ValueError`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of detail for the second paragraph. Users might find it easier to understand how to create complex numbers if the REPL examples came before this full specification

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the example of float() which has even more detailed description of the input format in the second paragraph. I merged it with a note about whitespaces, so all information related to parsing string to complex is now in one place.

I would be happy to make the description shorter, even if less detailed. Do you have any suggestions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, though I would honestly also be inclined to move the float() examples higher up as well. I think it's probably correct to provide a full specification here; I just worry that it could overwhelmingly technical for Python beginners, who will probably learn best by looking at the examples.

In any event, I don't have a strong opinion; I'm happy to defer to you and Mark

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @AlexWaygood has a good point here: for readability, it would be nice to see just three examples (one for each of the three signatures) following the initial "Convert a single string or number to a complex number, or ..." line, before we get into the heavy detail of exactly which strings are permitted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved examples for complex() and float() and added examples for int().


If the argument is a number, the constructor serves as a numeric
conversion like :class:`int` and :class:`float`.
For a general Python object ``x``, ``complex(x)`` delegates to
``x.__complex__()``. If :meth:`~object.__complex__` is not defined then it falls back
to :meth:`~object.__float__`. If :meth:`!__float__` is not defined then it falls back
``x.__complex__()``.
If :meth:`~object.__complex__` is not defined then it falls back
to :meth:`~object.__float__`.
If :meth:`!__float__` is not defined then it falls back
to :meth:`~object.__index__`.

.. note::
If two arguments are provided or keyword arguments are used, each argument
may be any numeric type (including complex).
If both arguments are real numbers, return a complex number with the real
component *real* and the imaginary component *imag*.
If both arguments are complex numbers, return a complex number with the real
component ``real.real-imag.imag`` and the imaginary component
``real.imag+imag.real``.
Comment on lines +435 to +437
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably this will mention the deprecation if/when #119620 is merged? Or is the plan to remove the mention of allowing complex altogether?

I'd be tempted to not even document this (mis)feature, on the basis that people who already know about it already know about it, and people who don't know about it shouldn't start using it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan was to leave this until the end of the deprecation period.

I would be happy to remove this, but then we will receive another reports about wrong documentation or behavior. The old real + imag*1j was more correct for input with finite components (and ignoring corner cases with negative zero).

"Each argument may be any numeric type (including complex)." is already in the current documentation, so it is late to hide this without also changing the behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best we can do is to put the deprecation note as close to this sentence, as possible:)

If one of arguments is a real number, only its real component is used in
the above expressions.

If all arguments are omitted, returns ``0j``.

When converting from a string, the string must not contain whitespace
around the central ``+`` or ``-`` operator. For example,
``complex('1+2j')`` is fine, but ``complex('1 + 2j')`` raises
:exc:`ValueError`.
Examples::

>>> complex('+1.23')
(1.23+0j)
>>> complex('-4.5j')
-4.5j
>>> complex('-1.23+4.5j')
(-1.23+4.5j)
>>> complex('\t( -1.23+4.5J )\n')
(-1.23+4.5j)
>>> complex('-Infinity+NaNj')
(-inf+nanj)
>>> complex(1.23)
(1.23+0j)
>>> complex(imag=-4.5)
-4.5j
>>> complex(-1.23, 4.5)
(-1.23+4.5j)

The complex type is described in :ref:`typesnumeric`.

Expand Down Expand Up @@ -682,13 +718,14 @@ are always available. They are listed here in alphabetical order.
elements of *iterable* for which *function* is false.


.. class:: float(x=0.0)
.. class:: float(number=0.0, /)
float(string, /)

.. index::
single: NaN
single: Infinity

Return a floating point number constructed from a number or string *x*.
Return a floating point number constructed from a number or a string.

If the argument is a string, it should contain a decimal number, optionally
preceded by a sign, and optionally embedded in whitespace. The optional
Expand Down Expand Up @@ -742,7 +779,7 @@ are always available. They are listed here in alphabetical order.
Grouping digits with underscores as in code literals is allowed.

.. versionchanged:: 3.7
*x* is now a positional-only parameter.
The parameter is now positional-only.

.. versionchanged:: 3.8
Falls back to :meth:`~object.__index__` if :meth:`~object.__float__` is not defined.
Expand Down Expand Up @@ -926,17 +963,19 @@ are always available. They are listed here in alphabetical order.
with the result after successfully reading input.


.. class:: int(x=0)
int(x, base=10)
.. class:: int(number=0, /)
int(string, /, base=10)

Return an integer object constructed from a number or a string, or return
``0`` if no arguments are given.

Return an integer object constructed from a number or string *x*, or return
``0`` if no arguments are given. If *x* defines :meth:`~object.__int__`,
``int(x)`` returns ``x.__int__()``. If *x* defines :meth:`~object.__index__`,
it returns ``x.__index__()``. If *x* defines :meth:`~object.__trunc__`,
If the argument defines :meth:`~object.__int__`,
``int(x)`` returns ``x.__int__()``. If the argument defines :meth:`~object.__index__`,
it returns ``x.__index__()``. If the argument defines :meth:`~object.__trunc__`,
it returns ``x.__trunc__()``.
For floating point numbers, this truncates towards zero.

If *x* is not a number or if *base* is given, then *x* must be a string,
If the argument is not a number or if *base* is given, then it must be a string,
:class:`bytes`, or :class:`bytearray` instance representing an integer
in radix *base*. Optionally, the string can be preceded by ``+`` or ``-``
(with no space in between), have leading zeros, be surrounded by whitespace,
Expand Down Expand Up @@ -966,7 +1005,7 @@ are always available. They are listed here in alphabetical order.
Grouping digits with underscores as in code literals is allowed.

.. versionchanged:: 3.7
*x* is now a positional-only parameter.
The first parameter is now positional-only.

.. versionchanged:: 3.8
Falls back to :meth:`~object.__index__` if :meth:`~object.__int__` is not defined.
Expand All @@ -977,7 +1016,7 @@ are always available. They are listed here in alphabetical order.
.. versionchanged:: 3.11
:class:`int` string inputs and string representations can be limited to
help avoid denial of service attacks. A :exc:`ValueError` is raised when
the limit is exceeded while converting a string *x* to an :class:`int` or
the limit is exceeded while converting a string to an :class:`int` or
when converting an :class:`int` into a string would exceed the limit.
See the :ref:`integer string conversion length limitation
<int_max_str_digits>` documentation.
Expand Down
9 changes: 6 additions & 3 deletions Objects/clinic/complexobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Objects/complexobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -911,14 +911,17 @@ complex.__new__ as complex_new
real as r: object(c_default="NULL") = 0
imag as i: object(c_default="NULL") = 0

Create a complex number from a real part and an optional imaginary part.
Create a complex number from a string or numbers.

This is equivalent to (real + imag*1j) where imag defaults to 0.
If a string is given, parse it as a complex number.
If a single number is given, convert it to a complex number.
If the 'real' or 'imag' arguments are given, create a complex number
with the specified real and imaginary components.
[clinic start generated code]*/

static PyObject *
complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
/*[clinic end generated code: output=b6c7dd577b537dc1 input=f4c667f2596d4fd1]*/
/*[clinic end generated code: output=b6c7dd577b537dc1 input=ff4268dc540958a4]*/
{
PyObject *tmp;
PyNumberMethods *nbr, *nbi = NULL;
Expand Down
Loading