Default __nonzero__ impl doesn't throw a TypeError exception

Discussion in 'Python' started by Sergey Kishchenko, Jan 8, 2009.

  1. In Python empty container equals False in 'if' statements:

    # prints "It's ok"
    if not []:
    print "It's ok"

    Let's create a simple Foo class:

    class Foo:
    pass

    Now I can use Foo objects in 'if' statements:

    #prints "Ouch!"
    f=Foo()
    if f:
    print "Ouch!"

    So, default __nonzero__ impl is to return True. I think, this
    behaviour conflicts with 'Explicit is better than implicit' and
    'Practicality beats purity' statements. I think, throwing a TypeError
    exception would be better. It will result in more explicit code with
    fewer errors.
    Sergey Kishchenko, Jan 8, 2009
    #1
    1. Advertising

  2. Sergey Kishchenko

    Terry Reedy Guest

    Sergey Kishchenko wrote:
    > In Python empty container equals False in 'if' statements:
    >
    > # prints "It's ok"
    > if not []:
    > print "It's ok"
    >
    > Let's create a simple Foo class:
    >
    > class Foo:
    > pass
    >
    > Now I can use Foo objects in 'if' statements:
    >
    > #prints "Ouch!"
    > f=Foo()
    > if f:
    > print "Ouch!"
    >
    > So, default __nonzero__ impl is to return True. I think, this
    > behaviour conflicts with 'Explicit is better than implicit' and
    > 'Practicality beats purity' statements. I think, throwing a TypeError
    > exception would be better. It will result in more explicit code with
    > fewer errors.


    Sensible (and documented) defaults pervade Python.
    Terry Reedy, Jan 8, 2009
    #2
    1. Advertising

  3. Sergey Kishchenko

    Chris Rebert Guest

    On Thu, Jan 8, 2009 at 5:53 AM, Sergey Kishchenko <> wrote:
    > In Python empty container equals False in 'if' statements:
    >
    > # prints "It's ok"
    > if not []:
    > print "It's ok"
    >
    > Let's create a simple Foo class:
    >
    > class Foo:
    > pass
    >
    > Now I can use Foo objects in 'if' statements:
    >
    > #prints "Ouch!"
    > f=Foo()
    > if f:
    > print "Ouch!"
    >
    > So, default __nonzero__ impl is to return True. I think, this
    > behaviour conflicts with 'Explicit is better than implicit' and
    > 'Practicality beats purity' statements. I think, throwing a TypeError
    > exception would be better. It will result in more explicit code with
    > fewer errors.


    Python has a rich notion of boolean truth compared to other languages.
    In this case, by default, non-None objects are considered True. It's a
    reasonable default behavior since wanting to differentiate between
    None and non-None objects is such a common task.
    Also, I've been programming in Python for a long while and have yet to
    encounter any bug due to this behavior.
    Regarding the Zen, on the contrary, this is a perfect example of
    "Practicality beats purity" in action.

    Cheers,
    Chris

    --
    Follow the path of the Iguana...
    http://rebertia.com
    Chris Rebert, Jan 8, 2009
    #3
  4. Sergey Kishchenko a écrit :
    > In Python empty container equals False in 'if' statements:


    Yes.

    > # prints "It's ok"
    > if not []:
    > print "It's ok"
    >
    > Let's create a simple Foo class:
    >
    > class Foo:
    > pass
    >
    > Now I can use Foo objects in 'if' statements:


    Yes.

    > #prints "Ouch!"
    > f=Foo()
    > if f:
    > print "Ouch!"
    >
    > So, default __nonzero__ impl is to return True.


    Yes. It's clearly documented FWIW.

    > I think, this
    > behaviour conflicts with 'Explicit is better than implicit'


    Why so ? It *is* explicit that the default for an object is to have a
    true value in a boolean context.

    > and
    > 'Practicality beats purity'


    Quite on the contrary. From a practical POV, the default truth values of
    Python objects are most of the time what you practically want in a
    boolean context - that is, any non-None object, non-empty sequence and
    non-zero numeric objects are true. __nonzero__ is here for the *very
    few* corner cases where this is not the sensible default.

    > statements. I think, throwing a TypeError
    > exception would be better. It will result in more explicit code with
    > fewer errors.


    I can understand that you've been bitten by the rules regarding truth
    values of Python objects. But if so, please remember that it's only
    because *you* assumed something different from what's documented.
    Bruno Desthuilliers, Jan 8, 2009
    #4
  5. On 8 ÑÎ×, 22:03, "Chris Rebert" <> wrote:
    > On Thu, Jan 8, 2009 at 5:53 AM, Sergey Kishchenko <> wrote:
    > > In Python empty container equals False in 'if' statements:

    >
    > > # prints "It's ok"
    > > if not []:
    > > š šprint "It's ok"

    >
    > > Let's create a simple Foo class:

    >
    > > class Foo:
    > > š špass

    >
    > > Now I can use Foo objects in 'if' statements:

    >
    > > #prints "Ouch!"
    > > f=Foo()
    > > if f:
    > > š šprint "Ouch!"

    >
    > > So, default __nonzero__ impl is to return True. I think, this
    > > behaviour conflicts with 'Explicit is better than implicit' and
    > > 'Practicality beats purity' statements. I think, throwing a TypeError
    > > exception would be better. šIt will result in more explicit code with
    > > fewer errors.

    >
    > Python has a rich notion of boolean truth compared to other languages.
    > In this case, by default, non-None objects are considered True. It's a
    > reasonable default behavior since wanting to differentiate between
    > None and non-None objects is such a common task.
    > Also, I've been programming in Python for a long while and have yet to
    > encounter any bug due to this behavior.
    > Regarding the Zen, on the contrary, this is a perfect example of
    > "Practicality beats purity" in action.
    >
    > Cheers,
    > Chris
    >
    > --
    > Follow the path of the Iguana...http://rebertia.com


    I agree with you. I completely forget about differentiating between
    None and non-None objects. I think, thread can be closed.
    Sergey Kishchenko, Jan 9, 2009
    #5
  6. Bruno Desthuilliers a écrit :
    > Sergey Kishchenko a écrit :

    (snip)
    >> #prints "Ouch!"
    >> f=Foo()
    >> if f:
    >> print "Ouch!"
    >>
    >> So, default __nonzero__ impl is to return True.

    >
    > Yes. It's clearly documented FWIW.


    To be more exact: there's no "default __nonzero__". The boolean value of
    an object is eval'd this way:

    If the object is None (special cased by the interpreter AFAICT), it is
    false.
    Else if the object implements __nonzero__, it has the boolean value
    returned by __nonzero__.
    Else if the object implements __len__, it has the boolean value of its
    length.
    Else if is true.

    >> I think, this
    >> behaviour conflicts with 'Explicit is better than implicit'

    >
    > Why so ? It *is* explicit that the default for an object is to have a
    > true value in a boolean context.


    I meant "explicit because documented", of course.


    >> and
    >> 'Practicality beats purity'

    >
    > Quite on the contrary. From a practical POV, the default truth values of
    > Python objects are most of the time what you practically want in a
    > boolean context - that is, any non-None object, non-empty sequence and
    > non-zero numeric objects are true. __nonzero__ is here for the *very
    > few* corner cases where this is not the sensible default.



    >> statements. I think, throwing a TypeError
    >> exception would be better. It will result in more explicit code with
    >> fewer errors.



    As a last note wrt/ explicitness and practicality: Implementing your
    proposition, one would have to either put each and every boolean
    expression in a try/except block or "explicitly" define __nonzero__ for
    each and any class - most of the time (about 99.9999% I'd say)
    implementing it as to return True - and expect that *everybody* does so.

    So yes, from a "purity" POV, it might look "more explicit". But this
    would certainly not be practical at all.

    OTHO, since boolean algebra only knows two values, it's clear that what
    is not false is by definition true. So having a default value (true) and
    a way to override it (__len__ and __nonzero__) is perhaps less "pure",
    but really as explicit and much more practical.

    My 2 cents...
    Bruno Desthuilliers, Jan 9, 2009
    #6
  7. Sergey Kishchenko

    Terry Reedy Guest

    Bruno Desthuilliers wrote:

    >
    > To be more exact: there's no "default __nonzero__".


    In Py3, '__nonzero__' has been renamed to __bool__.
    All objects are instances of class object,
    which indeed has no __bool__ method to be a default
    (it does have default __hash__ and __eq__, etc).

    > The boolean value of
    > an object is eval'd this way:
    >
    > If the object is None (special cased by the interpreter AFAICT), it is
    > false.


    Must be, since None.__bool__ does not exist.

    > Else if the object implements __nonzero__, it has the boolean value
    > returned by __nonzero__.


    Leaving None aside, __bool__ is only needed for non-collection classes
    (which do not have __len__) if at least 1 instance of the class is
    intended to be False. For built-in classes, this means numbers.

    > Else if the object implements __len__, it has the boolean value of its
    > length.


    This method, interpreted more generally as 'object count', essentially
    defines collection classes.

    > Else if is true.


    Thank you for the clarification. Then the __new__ method of class bool
    would be implemented in Python as

    def __new__(cls, ob):
    if ob is None:
    return False
    elif hasattr(ob, '__bool__'):
    b = ob.__bool__()
    if b.__class__ is bool:
    return b
    else:
    raise TypeError("__bool__ should return bool, returned %s" %
    b.__class__)
    elif hasattr(ob, '__len__'):
    return len(ob) != 0
    # ob.__len__() != 0 does not do len's typecheck on return
    else:
    return True

    Experiments show that 3.0 bool.__new__ does typecheck the return of
    ob.__bool__ and raises the error indicated and that __len__ must return
    a value that passes len's typecheck.

    Terry Jan Reedy
    Terry Reedy, Jan 9, 2009
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kerri
    Replies:
    2
    Views:
    12,990
    Kevin Spencer
    Oct 27, 2003
  2. VisionSet
    Replies:
    51
    Views:
    4,253
    Tony Morris
    Jul 14, 2004
  3. Replies:
    15
    Views:
    7,475
    Roedy Green
    Sep 8, 2005
  4. Christian Eder

    __nonzero__ of iterators

    Christian Eder, Apr 1, 2004, in forum: Python
    Replies:
    4
    Views:
    326
    Raymond Hettinger
    Apr 5, 2004
  5. Emanuele D'Arrigo

    To throw or to throw not?

    Emanuele D'Arrigo, Nov 14, 2008, in forum: Python
    Replies:
    6
    Views:
    307
    Emanuele D'Arrigo
    Nov 15, 2008
Loading...

Share This Page