Skip to content
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

"and" operator tests the first argument twice #39578

Closed
amauryfa opened this issue Nov 21, 2003 · 7 comments
Closed

"and" operator tests the first argument twice #39578

amauryfa opened this issue Nov 21, 2003 · 7 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs)

Comments

@amauryfa
Copy link
Member

BPO 846564
Nosy @tim-one, @loewis, @amauryfa
Files
  • nonzero.py: script showing that "if a and b" can be broken.
  • nonzero.py: script showing that "if a and b" can be broken.
  • 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 2003-11-24.22:34:41.000>
    created_at = <Date 2003-11-21.13:08:39.000>
    labels = ['interpreter-core', 'invalid']
    title = '"and" operator tests the first argument twice'
    updated_at = <Date 2003-11-24.22:34:41.000>
    user = 'https://github.com/amauryfa'

    bugs.python.org fields:

    activity = <Date 2003-11-24.22:34:41.000>
    actor = 'tim.peters'
    assignee = 'none'
    closed = True
    closed_date = None
    closer = None
    components = ['Interpreter Core']
    creation = <Date 2003-11-21.13:08:39.000>
    creator = 'amaury.forgeotdarc'
    dependencies = []
    files = ['1105', '1106']
    hgrepos = []
    issue_num = 846564
    keywords = []
    message_count = 7.0
    messages = ['19068', '19069', '19070', '19071', '19072', '19073', '19074']
    nosy_count = 4.0
    nosy_names = ['tim.peters', 'loewis', 'nnorwitz', 'amaury.forgeotdarc']
    pr_nums = []
    priority = 'normal'
    resolution = 'not a bug'
    stage = None
    status = 'closed'
    superseder = None
    type = None
    url = 'https://bugs.python.org/issue846564'
    versions = []

    @amauryfa
    Copy link
    Member Author

    When the first operand of "and" results in False, its truth
    value is calculated again.

    Example:
    class myBool:
    def __init__(self,value):
    self.value = value
    def __nonzero__(self):
    print 'testing myBool(%s)' % self.value
    return bool(self.value)

    if myBool(0) and myBool(1):
      pass

    will print:
    testing myBool(0)
    testing myBool(0)

    The same thing occurs with the "or" operator, when the
    first argument has a True truth value:

    if myBool(2) and myBool(3):
      pass
    will print:
    testing myBool(2)
    testing myBool(2)

    This can be a problem when the "__nonzero__" function
    is slow or has side-effects. I agree this is not often the
    case...

    But imagine an object which truth value means "there
    are more data to read in a stream". If python evaluates
    __nonzero__ twice, the expression: "stream and
    otherTest()" can become True *without* evaluating the
    otherTest!

    @amauryfa amauryfa added invalid interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Nov 21, 2003
    @amauryfa amauryfa added invalid interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Nov 21, 2003
    @nnorwitz
    Copy link
    Mannequin

    nnorwitz mannequin commented Nov 21, 2003

    Logged In: YES
    user_id=33168

    Ouch! This happens in 2.2 and CVS (I assume 2.3 too). I'll
    look into this. Fixing this should be a good way to improve
    performance. :-)

    Thanks for the report!

    @tim-one
    Copy link
    Member

    tim-one commented Nov 21, 2003

    Logged In: YES
    user_id=31435

    Don't panic <wink>. "and" doesn't evaluate anything twice.
    The subtlety here is that "and" and "or" return one of their
    arguments. If x evaluates to false in "x and y", then "x and y"
    returns x:

    >>> class C:
    ...     def __nonzero__(self): return False
    ...
    >>> x, y = C(), C()
    >>> (x and y) is x
    True
    >>> (x or y) is y
    True
    >>>

    The second evaluation occurs because "if expr:" has to
    evaluate expr. That part's got nothing to do with "and", it's
    entirely to do with "if".

    None of this is going to change, of course.

    @amauryfa
    Copy link
    Member Author

    Logged In: YES
    user_id=389140

    I don't mind the __nonzero__ be called twice, but the test
    can be totally wrong if the value truth changes:

    import random
    class C:
      def __nonzero__(self):
        return bool(random.randint(0,1))
    
    if C() and False:
      print "Should we really allow this?"

    There are 25% chances for the condition to be true. I find
    this odd.
    OK, this kind of script is not likely to happen in real life.
    So I attached a script where the object is a kind of pipe; it is
    True if there are data in it. And while we only fill it with "1",
    we sometimes enter the block where a "2" is found!

    If this behaviour is expected, it should at least be clearly
    documented!
    This remind me the macros in C, where a "variable" can be
    evaluated several times, so we have to be aware of side-
    effects. I did not know that "if a and b" was such a macro...

    1 similar comment
    @amauryfa
    Copy link
    Member Author

    Logged In: YES
    user_id=389140

    I don't mind the __nonzero__ be called twice, but the test
    can be totally wrong if the value truth changes:

    import random
    class C:
      def __nonzero__(self):
        return bool(random.randint(0,1))
    
    if C() and False:
      print "Should we really allow this?"

    There are 25% chances for the condition to be true. I find
    this odd.
    OK, this kind of script is not likely to happen in real life.
    So I attached a script where the object is a kind of pipe; it is
    True if there are data in it. And while we only fill it with "1",
    we sometimes enter the block where a "2" is found!

    If this behaviour is expected, it should at least be clearly
    documented!
    This remind me the macros in C, where a "variable" can be
    evaluated several times, so we have to be aware of side-
    effects. I did not know that "if a and b" was such a macro...

    @loewis
    Copy link
    Mannequin

    loewis mannequin commented Nov 24, 2003

    Logged In: YES
    user_id=21627

    This is clearly not a bug. I'm closing it as "works for me":
    the "and" operator does *not* evaluate its argument twice
    (as Tim explains, and in contrast to what the subject claims).

    That the semantics of the if statement (not the "and"
    expression) is surprising if the result of __nonzero__
    changes in subsequent invokcations might be a fact (strictly
    speaking, whether you are surprised depends on what you
    expect). However, the behaviour of Python is not at all
    random in itself, and there are very good reasons for things
    being just the way they are.

    If changing zero-ness of objects surprises you: Don't do
    that, then.

    @tim-one
    Copy link
    Member

    tim-one commented Nov 24, 2003

    Logged In: YES
    user_id=31435

    Just noting that someone who creates objects with insanely
    surprising __nonzero__ behavior can easily avoid the "double
    evaluation" by coding

    if bool(x) and bool(y):

    instead. I agree with Martin that there's no bug here, so
    agree with his closing the bug report.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 9, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    interpreter-core (Objects, Python, Grammar, and Parser dirs)
    Projects
    None yet
    Development

    No branches or pull requests

    2 participants