Coding style

L

Lawrence D'Oliveiro

Bruno Desthuilliers said:
Python has a boolean type.

A _proper_ boolean type would _have_ to be used in conditionals.
There's nothing to assume, and nothing arbitrary in it. It's all clearly
defined in whole letters in the language references.

Not simply enough.
The fact that the expression is used in the context of a if statement is
clearly enough to denote a boolean expression.

Which is an inconsistent use of the term "boolean" compared to your
statement above that "Python has a boolean type", is it not?
Explicitly testing against a boolean is uselessly redundant...

Not sure this has anything with what I was saying.
 
G

Georg Brandl

Lawrence said:
One of my rules is, always program like the language actually has a Boolean
type, even if it doesn't. That means, never assume that arbitrary values
can be interpreted as true or false, always put in an explicit comparison
if necessary so it's obvious the expression is a Boolean.

You can do that, but it's not considered Pythonic. And it might be ineffective.

Other than in PHP, Python has clear rules when an object of a builtin type
is considered false (i.e. when it's empty). So why not take advantage of
this?

Georg
 
B

Boris Borcic

PTY said:
Which is better?

lst = [1,2,3,4,5]

while lst:
lst.pop()

OR

while len(lst) > 0:
lst.pop()

allways that either-or stuff ! And why did you not consider

while len(lst) : list.pop()

a neat middle ground, wouldn't you say ?

Cheers, BB
 
B

Boris Borcic

Bruno said:
empty_list = []
bool(empty_list) is False
=> True

it's just a pity that the symmetric expression

list(False) is []

doesn't hold.

I guess the problem is that if list(False) was thus defined, it would be
difficult not to define list(True). And then the zen of Python clashes

"In the presence of ambiguity, refuse the temptation to guess".

OTOH, my favorite there would be

list(True) is [None]

together with

list(n) == n*[None] for all positive integers n

Cheers, BB
 
C

Christophe

Patrick Maupin a écrit :
The perverse wish, expressed in the specific example, that SOME piece
of code SOMEWHERE should PLEASE throw an exception because some idiot
passed a generator expression rather than a list into a function, is
not apt to be well received by an audience which strives for generality
when it makes sense

Well then, you haven't beed using enouth generator expressions or else,
that kind of mistake would have bitten you badly by now and you would
agree with that remark !
confronted with the wished-for exception, would fix the function so
that it quite happily accepted generator expressions, rather than
changing a conditional to use len() just so that an equivalent
exception could happen a bit earlier.

Thanks but NO. If that function needs to iterate twice on the
expression, then it needs to iterate twice and passing it a generator
will only cause strange bugs.
 
M

Marc 'BlackJack' Rintsch

Bruno said:
empty_list = []
bool(empty_list) is False
=> True

it's just a pity that the symmetric expression

list(False) is []

doesn't hold.

You want the empty list to be a singleton!? And I don't find
`list(False)` to return an empty list be very obvious.
I guess the problem is that if list(False) was thus defined, it would be
difficult not to define list(True). And then the zen of Python clashes

"In the presence of ambiguity, refuse the temptation to guess".

OTOH, my favorite there would be

list(True) is [None]

Wow it even gets better, the list containing one `None` object should be a
singleton too. Argh.

Ciao,
Marc 'BlackJack' Rintsch
 
B

Boris Borcic

Marc said:
Bruno said:
empty_list = []
bool(empty_list) is False
=> True
it's just a pity that the symmetric expression

list(False) is []

doesn't hold.

You want the empty list to be a singleton!?

Oops,

list(False) == [], then.

BTW, be careful with 'singleton' when applying to aggregates - it took me a
while to figure out you did not mean an obj X s.t. len(X)==1.

And I don't find
`list(False)` to return an empty list be very obvious.

What would be your choice ?
 
A

Antoon Pardon

You can do that, but it's not considered Pythonic. And it might be ineffective.

Other than in PHP, Python has clear rules when an object of a builtin type
is considered false (i.e. when it's empty). So why not take advantage of
this?

Because it doesn't always do what I want.

I once had a producer consumer code. When the client asked whether new
items were available the function could return three different values

1) a list with items, to be consumed
2) an empty list (meaning there were no items available for the
moment but there could be in the future
3) None (meaning the producer was done)

Just testing for the truth value of the returned result in order
to see whether the client should continue or not would often
have made the client exit prematurely.

IME such cases where testing for the truth value of an object
don't give the expected result, happen often enough to make me
carefully think about what I want to test for and then explicitly
do so.
 
M

Marc 'BlackJack' Rintsch

What would be your choice ?

``list()`` or ``[]`` for empty lists and a `TypeError` for
``list(False)``. Just like it is right now.

Ciao,
Marc 'BlackJack' Rintsch
 
P

Patrick Maupin

Christophe said:
Well then, you haven't beed using enouth generator expressions or else,
that kind of mistake would have bitten you badly by now and you would
agree with that remark !

I use generators frequently.
Thanks but NO. If that function needs to iterate twice on the
expression, then it needs to iterate twice and passing it a generator
will only cause strange bugs.

The original post did not say "this function is iterating twice over
the same data." It only said that there might be a significant
computational cost on an empty iterator, and wished that the code would
somehow throw an exception to alert the programmer to this cost. I
maintain that if the cost is low enough that the programmer doesn't
notice it without the exception, there is no problem, and if the cost
is high enough that the programmer notices it, the profiler would find
the problem easily.

I'm not sure why you say "Thanks but NO" to the option of fixing the
function so that it accepts generators, but in any case if you know
up-front that a function needs to iterate twice over the same data and
you are too lazy to write some code to do that for generators (such as
by creating a list object or using the tee function), then by all means
feel free to check that the data coming into the function is not a
generator. But this is still not a good reason why, in the general
case, "if len(lst)" should be preferred over "if lst". In fact, I'm
not sure it's a good reason to use this construct even in this specific
case. It would be too easy for someone to "fix" this during
refactoring. If you really want to make sure that a function can only
take objects which have __len__ methods, why not come right out and say
so:

assert hasattr(lst, "__len__")

Regards,
Pat
 
D

Dennis Lee Bieber

A _proper_ boolean type would _have_ to be used in conditionals.
Be glad you never worked FORTRAN on (Open)VMS... system error codes
were commonly tested as

if (error_code) then
print "informational message"
else
print "error message"
end if

where the Boolean test was on LSB -- IE, odd numbers were
success/information values, even numbers were warning/error values

I doubt you could persuade anyone that the above should be

if (iand(error_code, 0x01) .eq. 0x01) then
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
D

Dennis Lee Bieber

I once had a producer consumer code. When the client asked whether new
items were available the function could return three different values

1) a list with items, to be consumed
2) an empty list (meaning there were no items available for the
moment but there could be in the future
3) None (meaning the producer was done)
You have a documented interface with a tri-state return... For this
situation, you would need the explicit test... I'd probably end up with
something like

while True:
retrn = function()
if retrn is None:
break
elif retrn:
consume

--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
G

Georg Brandl

Antoon said:
Because it doesn't always do what I want.

I once had a producer consumer code. When the client asked whether new
items were available the function could return three different values

1) a list with items, to be consumed
2) an empty list (meaning there were no items available for the
moment but there could be in the future
3) None (meaning the producer was done)

Just testing for the truth value of the returned result in order
to see whether the client should continue or not would often
have made the client exit prematurely.

IME such cases where testing for the truth value of an object
don't give the expected result, happen often enough to make me
carefully think about what I want to test for and then explicitly
do so.

You're right, carefully thinking is always a good thing. Not using a
language feature just because it would fail in another case is not.

Georg
 
C

Carl Banks

Patrick said:
The original post did not say "this function is iterating twice over
the same data." It only said that there might be a significant
computational cost on an empty iterator, and wished that the code would
somehow throw an exception to alert the programmer to this cost.

You're misrepresenting what I said. Cost was merely the
best-case-scenario. Bugs could arise if, say, the finalization
silently depends on non-empty iterable.

But this is still not a good reason why, in the general
case, "if len(lst)" should be preferred over "if lst".

Whatever. The OP asked for a reason one should be preferred over the
other; this was simply one reason to use "if len(lst)>0". Not good
enough for you? Fine, you still have all the reasons to continue to
use "if lst" (as opposed to just bashing "if len(lst)>0") that you and
others have shared with us in this thread.

(Wait a second....)


Carl Banks
 
D

Donn Cave

Lawrence D'Oliveiro wrote:

You can do that, but it's not considered Pythonic. And it might be
ineffective.

Other than in PHP, Python has clear rules when an object of a builtin type
is considered false (i.e. when it's empty). So why not take advantage of
this?

I don't know whether she would welcome this or not, but here
I provide an archive link to a classic post by Laura Creighton
on this matter:

http://groups.google.com/group/comp.lang.python/msg/2de5e1c8384c0360

It's lengthy but very readable, and for me it has that quality of
exposition where you feel at first reading as though you had
already known all that -- even if you really hadn't.

But I don't know where she is today, or the Python she was
writing about.

Donn Cave, (e-mail address removed)
 
P

Patrick Maupin

Terry said:
Given that the input is going to be scanned by an iterator, it really makes
sense to me to accept an iterator as input. (Unless the function is for
special-case usage in a predefined, never to be expanded, set of
circumstances in which such generality is not needed. In such a case, if
lst: is no problem.) I think the following, untested rewrite suffices.

def process_values(lst):
it = iter(lst)
try:
item = it.next()
do_expensive_initialization_step()
do_something_with(item) # see note
for item in it:
do_something_with(item)
do_expensive_finalization_step()
except StopIteration:
pass # or any special empty input code

# note: if writing do_something_with(item) is bothersome,
# replace 3 lines with
while True:
do_something_with(item)
try:
item = it.next
except StopIteration:
break

In general, to work with iterators instead of containers, change

if container:
do_stuff()
else:
do_empty()

to

try:
item = it.next()
do_modified_stuff() # taking into account that have item already
except StopIteration:
do_empty()

Good example. But if you want, you can also do the same thing
without explicitly dealing with StopIteration exceptions, by (ab)using
the for statment to deal with the StopIteration exceptions implicitly
for you:

def process_values(lst):
it = iter(lst)
# This for statement executes 0 or 1 times
for item in it:
do_expensive_initialization_step()
do_something_with(item)
break
else:
# Empty list code, if needed, goes here...
return
# This for statement executes max(0, len(lst) - 1) times
for item in it:
do_something_with(item)
do_expensive_finalization_step()

And, of course, if you really want to defer optimizations, and you
expect the code to not be in a critical speed path and to not be passed
huge lists or iterators, you could take the original function and add:

lst = list(lst)

as the first line. Frankly, if I had a working function in a piece of
code, and then a failure because it was being called with a generator,
I would find this very tempting unless I strongly suspected it would
cause issues later.

Regards,
Pat
 
L

Lawrence D'Oliveiro

You can do that, but it's not considered Pythonic. And it might be
ineffective.

Other than in PHP, Python has clear rules when an object of a builtin type
is considered false (i.e. when it's empty). So why not take advantage of
this?

Because the clearest rule of all is that True is true, and False is false,
and that's all I want to have to remember.
 
A

Antoon Pardon

You're right, carefully thinking is always a good thing. Not using a
language feature just because it would fail in another case is not.

I would say the opposite is just as true. Using a language feature
just because it happens to give the right answer, is not a good thing.
You don't know how your code will evolve and my experience is that the
lesser you use this feature, the lesser the chance you will have
to track a nasty bug later.
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top