Skip to content

bool(~True) == True #82012

Description

@tomerv
mannequin
BPO 37831
Nosy @gvanrossum, @tim-one, @rhettinger, @mdickinson, @serhiy-storchaka, @eryksun, @tomerv
Superseder
  • bpo-12447: ~True is not False
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2019-08-12.20:25:46.266>
    created_at = <Date 2019-08-12.10:48:52.558>
    labels = ['interpreter-core', 'type-feature', '3.7']
    title = 'bool(~True) == True'
    updated_at = <Date 2019-08-14.17:45:40.127>
    user = 'https://github.com/tomerv'

    bugs.python.org fields:

    activity = <Date 2019-08-14.17:45:40.127>
    actor = 'gvanrossum'
    assignee = 'none'
    closed = True
    closed_date = <Date 2019-08-12.20:25:46.266>
    closer = 'rhettinger'
    components = ['Interpreter Core']
    creation = <Date 2019-08-12.10:48:52.558>
    creator = 'tomerv'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 37831
    keywords = []
    message_count = 24.0
    messages = ['349452', '349459', '349461', '349477', '349478', '349480', '349482', '349484', '349487', '349488', '349489', '349493', '349494', '349495', '349508', '349509', '349522', '349628', '349656', '349661', '349707', '349709', '349723', '349724']
    nosy_count = 7.0
    nosy_names = ['gvanrossum', 'tim.peters', 'rhettinger', 'mark.dickinson', 'serhiy.storchaka', 'eryksun', 'tomerv']
    pr_nums = []
    priority = 'normal'
    resolution = 'duplicate'
    stage = 'resolved'
    status = 'closed'
    superseder = '12447'
    type = 'enhancement'
    url = 'https://bugs.python.org/issue37831'
    versions = ['Python 3.7']

    Linked PRs

    Activity

    1. tomerv commented on Aug 12, 2019

      tomervmannequin
      MannequinAuthor

      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.

    2. mdickinson commented on Aug 12, 2019

      @mdickinson
      Member

      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:

      >>> import numpy as np
      >>> ~np.bool_(True)
      False
      >>> ~np.bool_(False)
      True

      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.

    3. mdickinson commented on Aug 12, 2019

      @mdickinson
      Member

      Looks like this is essentially a duplicate of bpo-12447

    4. rhettinger commented on Aug 12, 2019

      @rhettinger
      Contributor

      +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.

    5. mdickinson commented on Aug 12, 2019

      @mdickinson
      Member

      See also the discussion started by Antoine here:

      https://mail.python.org/pipermail//python-ideas/2016-April/039488.html

    6. serhiy-storchaka commented on Aug 12, 2019

      @serhiy-storchaka
      Member

      -1 from me too.

      Making True == -1 looks interesting, but it has drawbacks.

    7. mdickinson commented on Aug 12, 2019

      @mdickinson
      Member

      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.

    8. mdickinson commented on Aug 12, 2019

      @mdickinson
      Member

      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)
      True
    9. tim-one commented on Aug 12, 2019

      @tim-one
      Member

      Mark, isn't int() the obvious way "to convert an integer-like thing to an actual int"?

      >>> int(True)
      1
      >>> int(False)
      0

      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.

    10. serhiy-storchaka commented on Aug 12, 2019

      @serhiy-storchaka
      Member

      For reference, the link to the Python-Ideas discussion in Mailman 3:

      https://mail.python.org/archives/list/python-ideas@python.org/thread/7UR3XGXNLGCM6QFR7KTIQ2QGVRS6QNZH/

    11. 21 remaining items

    12. added
      3.12only security fixes
      and removed on Sep 28, 2022
    13. iritkatriel commented on Apr 4, 2023

      @iritkatriel
      Member

      @timhoffm The deadline for getting this into 3.12 is in about a month.

    14. timhoffm commented on Apr 8, 2023

      @timhoffm
      Contributor

      Thanks for the reminder. I may get around this next week.

    15. timhoffm commented on Apr 28, 2023

      @timhoffm
      Contributor

      @iritkatriel the PR #103487 is ready. Can I still help with anything to get this into 3.12?

    16. added 5 commits that reference this issue on Apr 28, 2023
    17. added a commit that references this issue on May 3, 2023
    18. pochmann3 commented on Apr 4, 2024

      @pochmann3
      Contributor

      I don't think I understand why anyone would want to use | and & on bools to get another bool, instead of just using or and and.

      I think I've had cases where I wanted a bool and needed both operands evaluated, so or and and wouldn't have worked.

    19. terryjreedy commented on Aug 29, 2024

      @terryjreedy
      Member
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.12only security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-featureA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions