mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
gh-82012: Deprecate bitwise inversion (~) of bool (#103487)
The bitwise inversion operator on bool returns the bitwise inversion of the underlying int value; i.e. `~True == -2` such that `bool(~True) == True`. It's a common pitfall that users mistake `~` as negation operator and actually want `not`. Supporting `~` is an artifact of bool inheriting from int. Since there is no real use-case for the current behavior, let's deprecate `~` on bool and later raise an error. This removes a potential source errors for users. Full reasoning: https://github.com/python/cpython/issues/82012#issuecomment-1258705971 Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
This commit is contained in:
parent
5b05b013ff
commit
fdb3ef8c0f
6 changed files with 78 additions and 25 deletions
|
@ -147,7 +147,7 @@ are always available. They are listed here in alphabetical order.
|
||||||
or omitted, this returns ``False``; otherwise, it returns ``True``. The
|
or omitted, this returns ``False``; otherwise, it returns ``True``. The
|
||||||
:class:`bool` class is a subclass of :class:`int` (see :ref:`typesnumeric`).
|
:class:`bool` class is a subclass of :class:`int` (see :ref:`typesnumeric`).
|
||||||
It cannot be subclassed further. Its only instances are ``False`` and
|
It cannot be subclassed further. Its only instances are ``False`` and
|
||||||
``True`` (see :ref:`bltin-boolean-values`).
|
``True`` (see :ref:`typebool`).
|
||||||
|
|
||||||
.. index:: pair: Boolean; type
|
.. index:: pair: Boolean; type
|
||||||
|
|
||||||
|
|
|
@ -802,6 +802,39 @@ number, :class:`float`, or :class:`complex`::
|
||||||
hash_value = -2
|
hash_value = -2
|
||||||
return hash_value
|
return hash_value
|
||||||
|
|
||||||
|
.. _typebool:
|
||||||
|
|
||||||
|
Boolean Type - :class:`bool`
|
||||||
|
============================
|
||||||
|
|
||||||
|
Booleans represent truth values. The :class:`bool` type has exactly two
|
||||||
|
constant instances: ``True`` and ``False``.
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
single: False
|
||||||
|
single: True
|
||||||
|
pair: Boolean; values
|
||||||
|
|
||||||
|
The built-in function :func:`bool` converts any value to a boolean, if the
|
||||||
|
value can be interpreted as a truth value (see section :ref:`truth` above).
|
||||||
|
|
||||||
|
For logical operations, use the :ref:`boolean operators <boolean>` ``and``,
|
||||||
|
``or`` and ``not``.
|
||||||
|
When applying the bitwise operators ``&``, ``|``, ``^`` to two booleans, they
|
||||||
|
return a bool equivalent to the logical operations "and", "or", "xor". However,
|
||||||
|
the logical operators ``and``, ``or`` and ``!=`` should be preferred
|
||||||
|
over ``&``, ``|`` and ``^``.
|
||||||
|
|
||||||
|
.. deprecated:: 3.12
|
||||||
|
|
||||||
|
The use of the bitwise inversion operator ``~`` is deprecated and will
|
||||||
|
raise an error in Python 3.14.
|
||||||
|
|
||||||
|
:class:`bool` is a subclass of :class:`int` (see :ref:`typesnumeric`). In
|
||||||
|
many numeric contexts, ``False`` and ``True`` behave like the integers 0 and 1, respectively.
|
||||||
|
However, relying on this is discouraged; explicitly convert using :func:`int`
|
||||||
|
instead.
|
||||||
|
|
||||||
.. _typeiter:
|
.. _typeiter:
|
||||||
|
|
||||||
Iterator Types
|
Iterator Types
|
||||||
|
@ -5394,27 +5427,6 @@ information. There is exactly one ``NotImplemented`` object.
|
||||||
It is written as ``NotImplemented``.
|
It is written as ``NotImplemented``.
|
||||||
|
|
||||||
|
|
||||||
.. _bltin-boolean-values:
|
|
||||||
|
|
||||||
Boolean Values
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Boolean values are the two constant objects ``False`` and ``True``. They are
|
|
||||||
used to represent truth values (although other values can also be considered
|
|
||||||
false or true). In numeric contexts (for example when used as the argument to
|
|
||||||
an arithmetic operator), they behave like the integers 0 and 1, respectively.
|
|
||||||
The built-in function :func:`bool` can be used to convert any value to a
|
|
||||||
Boolean, if the value can be interpreted as a truth value (see section
|
|
||||||
:ref:`truth` above).
|
|
||||||
|
|
||||||
.. index::
|
|
||||||
single: False
|
|
||||||
single: True
|
|
||||||
pair: Boolean; values
|
|
||||||
|
|
||||||
They are written as ``False`` and ``True``, respectively.
|
|
||||||
|
|
||||||
|
|
||||||
.. _typesinternal:
|
.. _typesinternal:
|
||||||
|
|
||||||
Internal Objects
|
Internal Objects
|
||||||
|
|
|
@ -710,6 +710,12 @@ Deprecated
|
||||||
replaced by :data:`calendar.Month.JANUARY` and :data:`calendar.Month.FEBRUARY`.
|
replaced by :data:`calendar.Month.JANUARY` and :data:`calendar.Month.FEBRUARY`.
|
||||||
(Contributed by Prince Roshan in :gh:`103636`.)
|
(Contributed by Prince Roshan in :gh:`103636`.)
|
||||||
|
|
||||||
|
* The bitwise inversion operator (``~``) on bool is deprecated. It will throw an
|
||||||
|
error in Python 3.14. Use ``not`` for logical negation of bools instead.
|
||||||
|
In the rare case that you really need the bitwise inversion of the underlying
|
||||||
|
``int``, convert to int explicitly with ``~int(x)``. (Contributed by Tim Hoffmann
|
||||||
|
in :gh:`103487`.)
|
||||||
|
|
||||||
Pending Removal in Python 3.13
|
Pending Removal in Python 3.13
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,22 @@ class BoolTest(unittest.TestCase):
|
||||||
self.assertEqual(-True, -1)
|
self.assertEqual(-True, -1)
|
||||||
self.assertEqual(abs(True), 1)
|
self.assertEqual(abs(True), 1)
|
||||||
self.assertIsNot(abs(True), True)
|
self.assertIsNot(abs(True), True)
|
||||||
self.assertEqual(~False, -1)
|
with self.assertWarns(DeprecationWarning):
|
||||||
self.assertEqual(~True, -2)
|
# We need to put the bool in a variable, because the constant
|
||||||
|
# ~False is evaluated at compile time due to constant folding;
|
||||||
|
# consequently the DeprecationWarning would be issued during
|
||||||
|
# module loading and not during test execution.
|
||||||
|
false = False
|
||||||
|
self.assertEqual(~false, -1)
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
# also check that the warning is issued in case of constant
|
||||||
|
# folding at compile time
|
||||||
|
self.assertEqual(eval("~False"), -1)
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
true = True
|
||||||
|
self.assertEqual(~true, -2)
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
self.assertEqual(eval("~True"), -2)
|
||||||
|
|
||||||
self.assertEqual(False+2, 2)
|
self.assertEqual(False+2, 2)
|
||||||
self.assertEqual(True+2, 3)
|
self.assertEqual(True+2, 3)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
The bitwise inversion operator (``~``) on bool is deprecated.
|
||||||
|
It returns the bitwise inversion of the underlying ``int`` representation such that
|
||||||
|
``bool(~True) == True``, which can be confusing. Use ``not`` for logical negation
|
||||||
|
of bools. In the rare case that you really need the bitwise inversion of the underlying ``int``,
|
||||||
|
convert to int explicitly ``~int(x)``.
|
|
@ -73,6 +73,22 @@ bool_vectorcall(PyObject *type, PyObject * const*args,
|
||||||
|
|
||||||
/* Arithmetic operations redefined to return bool if both args are bool. */
|
/* Arithmetic operations redefined to return bool if both args are bool. */
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
bool_invert(PyObject *v)
|
||||||
|
{
|
||||||
|
if (PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||||
|
"Bitwise inversion '~' on bool is deprecated. This "
|
||||||
|
"returns the bitwise inversion of the underlying int "
|
||||||
|
"object and is usually not what you expect from negating "
|
||||||
|
"a bool. Use the 'not' operator for boolean negation or "
|
||||||
|
"~int(x) if you really want the bitwise inversion of the "
|
||||||
|
"underlying int.",
|
||||||
|
1) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyLong_Type.tp_as_number->nb_invert(v);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
bool_and(PyObject *a, PyObject *b)
|
bool_and(PyObject *a, PyObject *b)
|
||||||
{
|
{
|
||||||
|
@ -119,7 +135,7 @@ static PyNumberMethods bool_as_number = {
|
||||||
0, /* nb_positive */
|
0, /* nb_positive */
|
||||||
0, /* nb_absolute */
|
0, /* nb_absolute */
|
||||||
0, /* nb_bool */
|
0, /* nb_bool */
|
||||||
0, /* nb_invert */
|
(unaryfunc)bool_invert, /* nb_invert */
|
||||||
0, /* nb_lshift */
|
0, /* nb_lshift */
|
||||||
0, /* nb_rshift */
|
0, /* nb_rshift */
|
||||||
bool_and, /* nb_and */
|
bool_and, /* nb_and */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue