You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bitwise operators have inconsistent behavior when acting on bool values: (Python 3.7.4)
# "&" works like "and"
>>> True&True
True
>>> True&False
False
>>> False&False
False
# "|" works like "or"
>>> True|True
True
>>> True|False
True
>>> False|False
False
# "~" does not work like "not"!
>>> ~True
-2
>>> ~False
-1
The result of this is the a user might start working with "&" and "|" on bool values (for whatever reason) and it will work as expected. But then, when adding "~" to the mix, things start to break.
The proposal is to make "~" act like "not" on bool values, i.e. ~True will be False; ~False will be True.
I'm not sure if this has any negative impact on existing code. I don't expect any, but you can never know. If there is no objection to this change, I can even try to implement it myself an submit a patch.
For instances of int, ~ does bitwise negation (with the usual two's-complement with an infinite number of bits model that Python uses for all bitwise operations on arbitrary-precision integers).
And rightly or wrongly, True and False are instances of int, so it should be possible to use True almost anywhere you'd usually use 1, with no change in behaviour. The proposed change would give us True == 1 but ~True != ~1.
So I think we're stuck with the current behaviour.
Given a time machine, this could arguably be "fixed" by making True equal to -1 rather than 1 ... But absent that time machine, I'd expect some amount of breakage from the proposed change.
It's worth noting that NumPy's bool_ type does do this:
+0 This proposal may be worth re-considering. I've seen the problem arise in practice on multiple occasions. I suspect that it will continue to give people trouble.
Right now, a bool is-a int that 1) only has two singleton instances equal to zero and one, 2) has a different repr, and 3) has the & | and ^ operations redefined to return instances of bool.
I think we could also override the ~ operation. That would be a Liskov violation, making bools slightly less substitutable for ints, but it does so in a way that is intuitive and likely to match what a user intends when inverting a bool.
Making True == -1 looks interesting, but it has drawbacks.
Yes, please ignore that part of my post. :-) It shouldn't be considered seriously until a time machine turns up (and probably not even then).
My main worry with the proposed change is accidental breakage from the change in meaning. I've so far failed to find any examples of real-world functions that could/would be broken - the closest I've come is floating-point bit-pattern manipulation functions (constructing a bit-string from a sign, exponent and significand, where it's quite natural to treat the sign both as an "is_negative" boolean and as a 0-or-1 integer). But that case didn't involve a ~sign at any point, so it doesn't count.
Still, I have a nagging suspicion that such a function will turn up if we make this change.
Having ~True *not* be the same as ~1 feels like a bigger surprise to me than having ~True not be False; it breaks my simple mental model that bools always behave like ints in numeric contexts.
There's also the minor annoyance that there isn't currently an obvious safe way to convert an integer-like thing to an actual int, to make sure that bools do the right thing in a numeric context. operator.index *ought* to be that obvious way, but it leaves bools untouched.
Activity
tomerv commentedon Aug 12, 2019
Bitwise operators have inconsistent behavior when acting on bool values: (Python 3.7.4)
The result of this is the a user might start working with "&" and "|" on bool values (for whatever reason) and it will work as expected. But then, when adding "~" to the mix, things start to break.
The proposal is to make "~" act like "not" on bool values, i.e. ~True will be False; ~False will be True.
I'm not sure if this has any negative impact on existing code. I don't expect any, but you can never know. If there is no objection to this change, I can even try to implement it myself an submit a patch.
mdickinson commentedon Aug 12, 2019
For instances of
int,~does bitwise negation (with the usual two's-complement with an infinite number of bits model that Python uses for all bitwise operations on arbitrary-precision integers).And rightly or wrongly,
TrueandFalseare instances ofint, so it should be possible to useTruealmost anywhere you'd usually use1, with no change in behaviour. The proposed change would give usTrue == 1but~True != ~1.So I think we're stuck with the current behaviour.
Given a time machine, this could arguably be "fixed" by making
Trueequal to-1rather than1... But absent that time machine, I'd expect some amount of breakage from the proposed change.It's worth noting that NumPy's
bool_type does do this:But
np.bool_doesn't have the same "is-a" relationship with integers:>>> np.bool_.__mro__ (<class 'numpy.bool_'>, <class 'numpy.generic'>, <class 'object'>)IOW, -1 from me.
mdickinson commentedon Aug 12, 2019
Looks like this is essentially a duplicate of bpo-12447
rhettinger commentedon Aug 12, 2019
+0 This proposal may be worth re-considering. I've seen the problem arise in practice on multiple occasions. I suspect that it will continue to give people trouble.
Right now, a bool is-a int that 1) only has two singleton instances equal to zero and one, 2) has a different repr, and 3) has the & | and ^ operations redefined to return instances of bool.
I think we could also override the ~ operation. That would be a Liskov violation, making bools slightly less substitutable for ints, but it does so in a way that is intuitive and likely to match what a user intends when inverting a bool.
mdickinson commentedon Aug 12, 2019
See also the discussion started by Antoine here:
https://mail.python.org/pipermail//python-ideas/2016-April/039488.html
serhiy-storchaka commentedon Aug 12, 2019
-1 from me too.
Making True == -1 looks interesting, but it has drawbacks.
mdickinson commentedon Aug 12, 2019
Yes, please ignore that part of my post. :-) It shouldn't be considered seriously until a time machine turns up (and probably not even then).
My main worry with the proposed change is accidental breakage from the change in meaning. I've so far failed to find any examples of real-world functions that could/would be broken - the closest I've come is floating-point bit-pattern manipulation functions (constructing a bit-string from a sign, exponent and significand, where it's quite natural to treat the sign both as an "is_negative" boolean and as a 0-or-1 integer). But that case didn't involve a
~signat any point, so it doesn't count.Still, I have a nagging suspicion that such a function will turn up if we make this change.
Having ~True *not* be the same as ~1 feels like a bigger surprise to me than having ~True not be False; it breaks my simple mental model that bools always behave like ints in numeric contexts.
mdickinson commentedon Aug 12, 2019
There's also the minor annoyance that there isn't currently an obvious safe way to convert an integer-like thing to an actual int, to make sure that bools do the right thing in a numeric context. operator.index *ought* to be that obvious way, but it leaves bools untouched.
>>> operator.index(True) Truetim-one commentedon Aug 12, 2019
Mark, isn't
int()the obvious way "to convert an integer-like thing to an actual int"?For the rest, I'm -True on making ~ do something magical for bools inconsistent with what it does for ints. "is-a" is a promise.
serhiy-storchaka commentedon Aug 12, 2019
For reference, the link to the Python-Ideas discussion in Mailman 3:
https://mail.python.org/archives/list/python-ideas@python.org/thread/7UR3XGXNLGCM6QFR7KTIQ2QGVRS6QNZH/
21 remaining items
iritkatriel commentedon Apr 4, 2023
@timhoffm The deadline for getting this into 3.12 is in about a month.
timhoffm commentedon Apr 8, 2023
Thanks for the reminder. I may get around this next week.
timhoffm commentedon Apr 28, 2023
@iritkatriel the PR #103487 is ready. Can I still help with anything to get this into 3.12?
pythongh-82012: Deprecate bitwise inversion (~) of bool
pythongh-82012: Deprecate bitwise inversion (~) of bool
pythongh-82012: Deprecate bitwise inversion (~) of bool
pythongh-82012: Deprecate bitwise inversion (~) of bool
pythongh-82012: Deprecate bitwise inversion (~) of bool
gh-82012: Deprecate bitwise inversion (~) of bool (#103487)
pochmann3 commentedon Apr 4, 2024
I think I've had cases where I wanted a bool and needed both operands evaluated, so
orandandwouldn't have worked.Fix bitwise inversion not applying on Python bool
terryjreedy commentedon Aug 29, 2024
Futher discussion: https://discuss.python.org/t/bool-deprecation/62232