Default __nonzero__ impl doesn't throw a TypeError exception

  • Thread starter Sergey Kishchenko
  • Start date
S

Sergey Kishchenko

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

Terry Reedy

Sergey said:
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.
 
C

Chris Rebert

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
 
B

Bruno Desthuilliers

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

Sergey Kishchenko

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

I agree with you. I completely forget about differentiating between
None and non-None objects. I think, thread can be closed.
 
B

Bruno Desthuilliers

Bruno Desthuilliers a écrit :
Sergey Kishchenko a écrit : (snip)

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

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.


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

Terry Reedy

Bruno said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top