Implicit conversion to boolean in if and while statements

A

Andrew Berg

If you need three (or four, or fifty)
distinguishable states, then obviously boolean context will not solve
your problem. I never said it would.
That is the impression I got from this statement:
How you interpret some_variable = None depends on what some_variable
represents. If some_variable represents "number of jelly beans in a jar",
then that should be 0 if there is no jar.
But I guess you misunderstood (or were just picking at) the example.

Of course I can (and do) explicitly use "if x is not None" when testing
for None, but I don't want a bug being obscured because "if x" accepts
an erroneous value that it interprets as truthy or falsey. I could be
explicit when testing for things other than None, but apparently that's
un-Pythonic.

To put it in duck-typing terms, why should everything have to quack like
True or False? Sure, I can see why 1 quacks like True or [] quacks like
False, but I don't see why say, a Logger or function should quack like
either. Should a Thread object be True if it's been started and False
otherwise?

If it truly is about something vs. nothing, why is a NameError (or
AttributeError) raised when testing with an undefined variable? Being
undefined quacks like nothing, doesn't it?
 
S

Steven D'Aprano

Your entire reply is predicated on this idea that I was talking about
writing classes with this extra "isempty" method.

No. I was talking about having "isempty" be part of the collection
interface, and eliminating polymorphic bool conversion.

It already is part of the collection interface: it is spelled __nonzero__
(Python 2) or __bool__ (Python 3), and like all dunder methods, it is
called automatically for you when you use the right syntax:

# do this
x = n - len(y)
if x and y:
...

# don't do this
x = n.__sub__(y.__len__())
if x.__nonzero__() and y.__nonzero__():
...
 
S

Steven D'Aprano

To put it in duck-typing terms, why should everything have to quack like
True or False? Sure, I can see why 1 quacks like True or [] quacks like
False, but I don't see why say, a Logger or function should quack like
either.

The default behaviour is that every object is something, hence true-like,
unless explicitly coded to be treated as false-like. Since both loggers
and functions are objects, they are true-like unless the default is
overridden.

If you don't like that simple, consistent model for truthiness, feel free
to design your own language with a different model. Or you can use any
one of the dozens of other existing languages which do what you want.

Should a Thread object be True if it's been started and False
otherwise?

If you, the Thread class author, want it to be, you can make it so.

If it truly is about something vs. nothing, why is a NameError (or
AttributeError) raised when testing with an undefined variable? Being
undefined quacks like nothing, doesn't it?

Not really. It doesn't quack like anything.


Are you suggesting that if x doesn't exist, you want this behaviour?

.... else:
.... print('x is falsy')
.... print(x)
....
x is falsy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined


OH MAN, that would be SO AWESOME, we should like so do it!!!

(I'm being sarcastic, in case it's not obvious.)
 
J

John Nagle

This has probably been discussed before, but why is there an implicit
conversion to a boolean in if and while statements?

if not None:
print('hi')
prints 'hi' since bool(None) is False.

If this was discussed in a PEP, I would like a link to it. There are so
many PEPs, and I wouldn't know which ones to look through.

Converting 0 and 1 to False and True seems reasonable, but I don't see
the point in converting other arbitrary values.

Because Boolean types were an afterthought in Python. See PEP 285.
If a language starts out with a Boolean type, it tends towards
Pascal/Ada/Java semantics in this area. If a language backs
into needing a Boolean type, as Python and C did, it tends to have
the somewhat weird semantics of a language which can't quite decide
what's a Boolean. C and C++ have the same problem, for exactly the
same reason - boolean types were an afterthought there, too.

John Nagle
 
A

Andrew Berg

The default behaviour is that every object is something, hence true-like,
unless explicitly coded to be treated as false-like. Since both loggers
and functions are objects, they are true-like unless the default is
overridden.
I am aware of the default behavior, but the reason for it still eludes me.
Are you suggesting that if x doesn't exist, you want this behaviour?
I don't want that, but I am suggesting that it would be consistent with
the idea of "something or nothing".
 
C

Chris Angelico

I am aware of the default behavior, but the reason for it still eludes me.

There has to be something. This way, you can use None in place of any
object, in the same way that a null pointer would be used in C; any
object is true, None isn't. What other default makes more sense?

ChrisA
 
D

Devin Jeanpierre

It already is part of the collection interface: it is spelled __nonzero__
(Python 2) or __bool__ (Python 3), and like all dunder methods, it is
called automatically for you when you use the right syntax:

You're still ignoring what I actually said.

-- Devin
 
E

Ethan Furman

Andrew said:
To put it in duck-typing terms, why should everything have to quack like
True or False? Sure, I can see why 1 quacks like True or [] quacks like
False, but I don't see why say, a Logger or function should quack like
either. Should a Thread object be True if it's been started and False
otherwise?

True and False are red herrings. It is more appropriate to think that
True quacks like something and False like nothing than the other way 'round.

Maybe some examples from my own code will help:

DbfTable --> True if any records in table, False otherwise

DbfIndex --> True if any records in index, False otherwise

DbfList --> True if any records in list, False otherwise

DbfDate --> True if a date, False otherwise (could be eight spaces
instead of a real date)

DbfDateTime --> True if a datetime, False otherwise

DbfRecord --> True always

DbfRecordTemplate --> True always

DbfRecordVaporware --> False always

While I could have DbfRecord be False if, for example, it had no data
stored in it, I have no use case for that scenario so haven't bothered.
Also, at this point I am using the distinction of True/False with
regards to records to determine if I have a real record (True means a
record/template I can read/write, False means I don't).

If it truly is about something vs. nothing, why is a NameError (or
AttributeError) raised when testing with an undefined variable? Being
undefined quacks like nothing, doesn't it?

It's about /representing/ something vs. nothing. An undefined name isn't
representing anything (except a bug, of course ;).

~Ethan~
 
L

Laszlo Nagy

I don't want that, but I am suggesting that it would be consistent with
the idea of "something or nothing".
Don't confuse names and objects. You can only test the truth value of
objects. If you don't have a name in a namespace, then it means you
don't have a tool to have a reference to anything (including the False
object).

Using the same logic you could also say that not giving any condition to
the "if" statement should be evaluated as False:

if:
print "This never gets executed"

But it makes no sense.
 
T

Terry Reedy

I am aware of the default behavior, but the reason for it still eludes me.

Ultimately, because Guido's intuition said that the current behavior is
the best. I happen to agree on this one.

Quoting from elsewhere:
If it truly is about something vs. nothing, why is a NameError (or
AttributeError) raised when testing with an undefined variable?


Python does not have 'undefined variable' objects in the way that some
other languages do. There is nothing to test 'with'. 'if
undefined_name:' raises NameError because all uses of undefined_names
other that assignments do. The actually execution order is expression
first, then if. You can explicitly test "'some_name' in namespace" or
"hasattr(obj, 'somename') by turning the possibly undefined name into a
string object.
 
A

alex23

I am aware of the default behavior, but the reason for it still eludes me..

Because it makes it simple to distinguish between having an object and
not having one without having to explicitly test for it each time.

db = connect("my:db") # or None if the connection failed
if db:
<do something>

I find that usage to be incredibly intuitive.
 
R

Rick Johnson

[...]

If I insist on making a single object do duty for both the jar and the
jellybean count, then I need a "null jar object", and I probably end up
with something like this:

Jar(number_of_beans=None) => null jar object with jar.jellybeans = 0
Jar(number_of_beans=0) => normal jar object with jar.jellybeans = 0
Jar(number_of_beans=42) => normal jar object with jar.jellybeans = 42

and then my code becomes even more complicated and less understandable,
but hey, it's *my* code and I can do whatever damn-fool thing I like!

Indeed, but why would you create a jellybean jar and never put any jelly beans into the jar? If you are doing so because you want to loop over a number of objects and never get a NameError than you need to study up on a few language features you may be unaware of:

1. block: try/except
2. statement: if
3. function: hasattr(obj, name)

Creating /any/ object that is never utilized is code smell; I don't care how extravagant your explanations are either -- and boy did you go to some great efforts to explain this line of argument. I'm impressed! ;-)
 
R

Rick Johnson

If you know some English, its clear that if and while
create bool contexts.

Wrong. "if and "while" do not /create/ anything. On a syntactical level they merely /suggest/ to the reader that the following statement is expected to be a boolean value. It is the /statement/ itself that creates the booleanvalue, not the keywords!

Observe:

0 == 0 -> True
isinstance("5", int) -> False

You see, "if" and "while" don't create anything, in reality they merely execute a block of code depending on the value of the statement that follows the keyword. "if" and "while" are only *logical switches* and nothing more. You could write a simple quasi-example of "if" as a function like this:

def if_(value):
if not value:
return
# do_something_here


Those previous statements where /explicit/, and as such need no bool() function to resolve their Boolean values, however, consider the following /implicit/ conversions to Boolean:

[] -> False
[1] -> True
"" -> False
"1" -> True
0 -> False
1 -> True
2 -> True
etc...

It is my strong opinion that these types of implicit conversions are evil obfuscations of the truth. If we want to convert an object to a Boolean, then use the bool() function on that object:

bool([]) -> False
bool([1]) -> True
etc...

Heck! Why even have a damn bool function if you're never going to use it?
[If you know English but have not
studied logic the 'if/while' make sense whereas 'bool' is
gobbledygook]

And which Univeristy would you recommend for studying the intricacies of "gobbledygook"? ;-)
 
C

Chris Angelico

And which Univeristy would you recommend for studying the intricacies of "gobbledygook"? ;-)

Dunno, where'd you get your degree in logic?

*dives for cover*

ChrisA
 
R

Rick Johnson

Your entire reply is predicated on this idea that I was talking about
writing classes with this extra "isempty" method.

Steven's adherence to this implicit conversion is warping his comprehensionof your words. He is so accustomed to "guessing" that it has become secondnature for him.
No. I was talking about having "isempty" be part of the collection
interface, and eliminating polymorphic bool conversion.

Which i believe is a great idea!

GvR has always been reluctant to incorporate full OOP machinery for some reason. I am not suggesting that Python be 100% OOP, HELL NO! But collectionsshould have had an "isempty" method from the beginning. But the same argument could be made against len, any, all, etc...

But now we are opening a whole bag of cats. What about hasattr, getattr, setattr, type, dir, id, isinstance, issubclass, and many more that could be inherited directly from object; and they should be!

On the flip side i do believe int, float, str, tuple, list, dict... should remain as built-ins, and the obvious: help, input, globals, locals, vars, print, etc...

Python has too many built-ins. I think we need a PyWart on this subject.
 
R

Rick Johnson

Your entire reply is predicated on this idea that I was talking about
writing classes with this extra "isempty" method.

Steven's adherence to this implicit conversion is warping his comprehensionof your words. He is so accustomed to "guessing" that it has become secondnature for him.
No. I was talking about having "isempty" be part of the collection
interface, and eliminating polymorphic bool conversion.

Which i believe is a great idea!

GvR has always been reluctant to incorporate full OOP machinery for some reason. I am not suggesting that Python be 100% OOP, HELL NO! But collectionsshould have had an "isempty" method from the beginning. But the same argument could be made against len, any, all, etc...

But now we are opening a whole bag of cats. What about hasattr, getattr, setattr, type, dir, id, isinstance, issubclass, and many more that could be inherited directly from object; and they should be!

On the flip side i do believe int, float, str, tuple, list, dict... should remain as built-ins, and the obvious: help, input, globals, locals, vars, print, etc...

Python has too many built-ins. I think we need a PyWart on this subject.
 
R

Rick Johnson

Because it makes it simple to distinguish between having an object and

not having one without having to explicitly test for it each time.



db = connect("my:db") # or None if the connection failed

if db:

<do something>



I find that usage to be incredibly intuitive.
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top