By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,767 Members | 1,357 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,767 IT Pros & Developers. It's quick & easy.

Coding style

P: n/a
PTY
Which is better?

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

while lst:
lst.pop()

OR

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

Jul 17 '06 #1
Share this Question
Share on Google+
86 Replies


P: n/a
On 17 Jul 2006 08:56:34 -0700, PTY <ty*****@yahoo.comwrote:
Which is better?

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

while lst:
lst.pop()

OR

while len(lst) 0:
lst.pop()
How about:

lst = [1,2,3,4,5]
while lst:
lst.pop()

Or even just:

lst = []

;-)

--
Cheers,
Simon B,
si***@brunningonline.net,
http://www.brunningonline.net/simon/blog/
Jul 17 '06 #2

P: n/a
PTY wrote:
Which is better?

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

while lst:
lst.pop()

OR

while len(lst) 0:
lst.pop()
The former, without a doubt. It says exactly the same thing, since lst
can only be considered false when it is empty. Experienced Python
programmers would scratch their heads at your second formulation.

I doubt there's much in it from a time point of view (though I know as I
write this it will spur someone to use timeit.py to point out I am wrong).

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://holdenweb.blogspot.com
Recent Ramblings http://del.icio.us/steve.holden

Jul 17 '06 #3

P: n/a
lst = [1,2,3,4,5]
while lst:
lst.pop()

Or even just:

lst = []
Subtly different though...
>>while lst:
.... lst.pop()
....
5
4
3
2
1
>>lst2
[]
>>lst = [1,2,3,4,5]
lst2 = lst
lst = []
lst2
[1, 2, 3, 4, 5]
>>lst = [1,2,3,4,5]
lst2 = lst
del lst[:]
lst2
[]

The original while loop changes the actual list, reassigning it
to a new list prevents other items that reference that list from
accessing the changes. As shown above, I recommend

del lst[:]

which should be as fast as python will let one do it. (maybe?
again with those timeit guys... ;)

-tkc


Jul 17 '06 #4

P: n/a
Or even just:
>
lst = []

;-)
Indeed.

I'd say the second one. Empty lists are not false. They are empty. Long
live dedicated boolean data types.

Jul 17 '06 #5

P: n/a
PTY wrote:
Which is better?

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

while lst:
lst.pop()

OR

while len(lst) 0:
lst.pop()
I think the first one is better, but if all you are doing is removing
all the items in the list, this is definitely better:

lst = []

-Don
Jul 17 '06 #6

P: n/a
tac-tics wrote:
>>Or even just:

lst = []

;-)


Indeed.

I'd say the second one. Empty lists are not false. They are empty. Long
live dedicated boolean data types.
Take them off to where they belong!

I'll bet you still write

if a>3 == True:

don't you ;-)

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://holdenweb.blogspot.com
Recent Ramblings http://del.icio.us/steve.holden

Jul 17 '06 #7

P: n/a
tac-tics wrote:
>
I'd say the second one. Empty lists are not false. They are empty. Long
live dedicated boolean data types.
Uh, no, empty lists are False in a boolean context:

http://docs.python.org/lib/truth.html

-Don
Jul 17 '06 #8

P: n/a
Steve Holden wrote:
I'll bet you still write

if a>3 == True:

don't you ;-)
I'll second that.

if (a>3) == True:

is the correct way :-)

Peter
Jul 17 '06 #9

P: n/a
On Mon, 17 Jul 2006 17:09:32 +0100 in comp.lang.python, "Simon
Brunning" <si***@brunningonline.netwrote:

[...]
>
lst = [1,2,3,4,5]
while lst:
lst.pop()

Or even just:

lst = []
del lst[:]

is probably closer to what the OP wants...

Regards,
-=Dave

--
Change is inevitable, progress is not.
Jul 17 '06 #10

P: n/a
Peter Otten wrote:
Steve Holden wrote:
I'll bet you still write

if a>3 == True:

don't you ;-)

I'll second that.

if (a>3) == True:

is the correct way :-)

Peter
No, to be consistent you'll have to write

if ((a>3) == True) == True:

Oops, I mean,

if (((a>3) == True) == True) == True:

Umm, never mind.

Jul 17 '06 #11

P: n/a
PTY wrote:
Which is better?

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

while lst:
lst.pop()

OR

while len(lst) 0:
lst.pop()
A dozen posts, but nobody has posted the right
answer yet, so I will :-)

It doesn't matter -- use whichever you prefer (*)
This is an angels on the head of a pin issue.

(*) -- If your code is part of an existing body of
code that uses one or the other style consistently,
then you should do the same.

Jul 17 '06 #12

P: n/a
In article <ma***************************************@python. org>,
Steve Holden <st***@holdenweb.comwrote:
tac-tics wrote:
....
>I'd say the second one. Empty lists are not false. They are empty.
Long live dedicated boolean data types.
Take them off to where they belong!
Tac-tics is right, an empty list is not False.

Anyway, just for some variety, I think (2) is preferrable
to (1), as is the following

while 1:
try:
lst.pop()
except IndexError:
break

Rather than blindly apply familiar patterns to our work,
I think everyone would agree that coding style in matters
like this should follow the underlying point of the code.
In this case, the body of the test refers implicitly to
the length of the list, since .pop() -(list[a], list[:a])
where a is (len(list) - 1) It's therefore quite appropriate
for the test to be length.

Donn Cave, do**@u.washington.edu
Jul 17 '06 #13

P: n/a
Donn Cave wrote:
Tac-tics is right, an empty list is not False.
But that's not what he said. He said it was "not false." That's wrong.
It's false. It's just not False.

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
The meaning of life is that it stops.
-- Franz Kafka
Jul 17 '06 #14

P: n/a

<ru***@yahoo.comwrote in message
news:11**********************@m73g2000cwd.googlegr oups.com...
PTY wrote:
>Which is better?

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

while lst:
lst.pop()

OR

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

A dozen posts, but nobody has posted the right
answer yet, so I will :-)

It doesn't matter -- use whichever you prefer (*)
This is an angels on the head of a pin issue.

(*) -- If your code is part of an existing body of
code that uses one or the other style consistently,
then you should do the same.
I'd go even one step further. Turn it into English (or your favorite
non-computer language):

1. While list, pop.

2. While the length of the list is greater than 0, pop.

Which one makes more sense? Guess which one I like. CPU cycles be damned.
:)

Bob
Jul 17 '06 #15

P: n/a
PTY

Bob Greschke wrote:
<ru***@yahoo.comwrote in message
news:11**********************@m73g2000cwd.googlegr oups.com...
PTY wrote:
Which is better?

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

while lst:
lst.pop()

OR

while len(lst) 0:
lst.pop()
A dozen posts, but nobody has posted the right
answer yet, so I will :-)

It doesn't matter -- use whichever you prefer (*)
This is an angels on the head of a pin issue.

(*) -- If your code is part of an existing body of
code that uses one or the other style consistently,
then you should do the same.

I'd go even one step further. Turn it into English (or your favorite
non-computer language):

1. While list, pop.

2. While the length of the list is greater than 0, pop.

Which one makes more sense? Guess which one I like. CPU cycles be damned.
:)

Bob

It looks like there are two crowds, terse and verbose. I thought terse
is perl style and verbose is python style. BTW, lst = [] was not what
I was interested in :-) I was asking whether it was better style to
use len() or not.

Jul 17 '06 #16

P: n/a

dwelch91 wrote:
tac-tics wrote:

I'd say the second one. Empty lists are not false. They are empty. Long
live dedicated boolean data types.
Uh, no, empty lists are False in a boolean context:

http://docs.python.org/lib/truth.html

-Don
Perhaps I should have specified it like this:
>>empty_list = []
empty_list is not False
True

I'm well aware that both of these snippets does the same thing. I'm
just spouting my opinion that lists and integers are not tests, and in
an ideal world (Java??? X-) if statements support only boolean types.

DISCLAIMER: I do not promote the use of Java.

Jul 18 '06 #17

P: n/a
tac-tics wrote:
I'm well aware that both of these snippets does the same thing. I'm
just spouting my opinion that lists and integers are not tests, ...
No, but testing their Boolean nature _is_ a test. Aggregate objects in
Python are true if they are non-empty, and false if they are empty.
That is reasonable, not uncommon a convention, and quite useful for
exactly the situations you were talking about. That convention exists
_so that_ writing::

if aContainer:
... container is not empty ...

is meaningful and convenient. So the answer to the original question
was, "The first one." Feel free to write it the other way with an
explicit test, but it's not Pythonic.

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
Life is a toy made of glass; it appears to be of inestimable price,
but in reality it is very cheap. -- Pietro Aretino, 1537
Jul 18 '06 #18

P: n/a
PTY wrote:
It looks like there are two crowds, terse and verbose. I thought terse
is perl style and verbose is python style. BTW, lst = [] was not what
I was interested in :-) I was asking whether it was better style to
use len() or not.
It's not canonical Python to use len() in this case. From PEP 8:

- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

Yes: if not seq:
if seq:

No: if len(seq)
if not len(seq)

The whole reason that a sequence supports testing is exactly for this
scenario. This is not an afterthought -- it's a fundamental design
decision of the language.

Regards,
Pat

Jul 18 '06 #19

P: n/a

PTY wrote:
Which is better?

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

while lst:
lst.pop()

OR

while len(lst) 0:
lst.pop()
I'm going to go against general opinion and suggest using "len(lst)>0",
though this is not a good example of why I think that.

In practice, I'd say the former is less generic. No, that's not a
typo.

If you were to write a function that expects a list (and actually uses
the list interface, indexing and such, rather than merely passing it
off to another function), the function would almost certainly fail if
you were to pass in an integer or some other non-sequence type.
However, functions expecting a list have a decent chance of working if
you were to pass in a numpy array.

But if you were to write "if lst" instead of "if len(lst)>0", then
you've just ensured your function will fail for numpy arrays. But you
get no benefit in genericity, because non-sequence types almost
certainly wouldn't work anyways.

Therefore, the only time I'd recommend using "if lst" is if all you're
doing is passing it to other functions.
Carl Banks

Jul 18 '06 #20

P: n/a

Patrick Maupin wrote:
PTY wrote:
It looks like there are two crowds, terse and verbose. I thought terse
is perl style and verbose is python style. BTW, lst = [] was not what
I was interested in :-) I was asking whether it was better style to
use len() or not.

It's not canonical Python to use len() in this case. From PEP 8:

- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

Yes: if not seq:
if seq:

No: if len(seq)
if not len(seq)

The whole reason that a sequence supports testing is exactly for this
scenario. This is not an afterthought -- it's a fundamental design
decision of the language.
That might have made sense when Python and string, list, tuple were the
only sequence types around.

Nowadays, Python has all kinds of spiffy types like numpy arrays,
interators, generators, etc., for which "empty sequence is false" just
doesn't make sense. If Python had been designed with these types in
mind, I'm not sure "empty list is false" would have been part of the
language, let alone recommend practice.
Carl Banks

Jul 18 '06 #21

P: n/a
PTY wrote:
Which is better?

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

while lst:
lst.pop()

OR

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

Here's another reason not to use "if lst". Say you have a function
that looks like this:

def process_values(lst):
if not lst:
return
do_expensive_initialization_step()
for item in lst:
do_something_with(item)
do_expensive_finalization_step()

That works, right? No problem, right?

What if you called the function like this:

process_values(x.strip() for x in values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty). Maybe some
subtle bugs introduced. If we're lucky, the finalization step will
throw an exception.

If we had used "if len(list)>0", we'd have gotten a nice exception
telling us that a generator is not welcome. Then we'd have changed the
argument to a list comprehension, or better, changed the function to
work for any iterator.
Carl Banks

Jul 18 '06 #22

P: n/a
"Carl Banks" <pa************@gmail.comwrites:
What if you called the function like this:

process_values(x.strip() for x in values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty). Maybe some
subtle bugs introduced. If we're lucky, the finalization step will
throw an exception.
You've got a function written to take a list arg and are passing it
something other than a list. Why do you expect it to work?
As soon as the function uses lst[3] for something, it will crash
if you pass it a sequence like that. Your example is mainly an
argument for static type checking.
Jul 18 '06 #23

P: n/a
Paul Rubin wrote:
"Carl Banks" <pa************@gmail.comwrites:
What if you called the function like this:

process_values(x.strip() for x in values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty). Maybe some
subtle bugs introduced. If we're lucky, the finalization step will
throw an exception.

You've got a function written to take a list arg and are passing it
something other than a list. Why do you expect it to work?
I don't expect it to work as is. Disregarding the test for emptiness
at the beginning, I would expect it to work for any iterable, even if
the author only intended it for a list. That's the problem: it looks
like it should work for any iterable, but it doesn't. I would think
it's a pretty easy mistake to make. (I suspect lots of programmers
don't even realize that empty generator expressions are true. Even if
they do, it's easy to overlook that test if you're not careful.)
As soon as the function uses lst[3] for something,
Notice that the function I wrote never indexes the list. It only
iterates.
it will crash
if you pass it a sequence like that.
The particular function I wrote might crash. Or it might merely do
unnecessary work. Or it might have subtle bugs. (OTOH, if it had used
the "if len(lst)>0", it would crash right away; no subtle bugs.)
Your example is mainly an
argument for static type checking.
Not really. I'm merely putting forth one argument for using "if
len(lst)>0" rather than "if lst". ISTM the scenerio I described isn't
terribly rare: you write a function that iterates over an iterable, and
carelessly use a test ("if lst") that doesn't work for all iterables,
and the result could lead to subtle bugs. Which is not as bad as
carelessly using a test ("if len(lst)>0") that doesn't work for all
iterables, and the result is an immediate exception.

I've given two pretty good reasons for using "if len(lst)>0": it allows
functions to be written generically for lists and numpy arrays, and it
catches the rather plausible mistake of testing the truth value an
iterable (which can lead to subtle bugs). This is contrasted to the
good reasons for using "if lst", which are... um... hmm... saving a few
keystrokes?
Carl Banks

Jul 18 '06 #24

P: n/a
Carl Banks wrote:
****def*process_values(lst):
********if*not*lst:
************return
********do_expensive_initialization_step()
********for*item*in*lst:
************do_something_with(item)
********do_expensive_finalization_step()
What if you called the function like this:

****process_values(x.strip()*for*x*in*values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty).**Maybe*some
subtle bugs introduced.**If*we're*lucky,*the*finalization*step *will
throw an exception.
The good news is that the above has a 99 percent chance that it just works
with iterators/generators -- even though the writer may not have been aware
of them/they didn't exist when the code was written...

Peter
Jul 18 '06 #25

P: n/a

Peter Otten wrote:
Carl Banks wrote:
def process_values(lst):
if not lst:
return
do_expensive_initialization_step()
for item in lst:
do_something_with(item)
do_expensive_finalization_step()
What if you called the function like this:

process_values(x.strip() for x in values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty). Maybe some
subtle bugs introduced. If we're lucky, the finalization step will
throw an exception.

The good news is that the above has a 99 percent chance that it just works
with iterators/generators -- even though the writer may not have been aware
of them/they didn't exist when the code was written...
There's a litmus test I like to use when justifying something with
percentages: I imagine that I'm presenting it to my boss, and ask
myself if I still expect to have a job the next day. :-)

Yes, I agree with you, it most cases I expect merely unnecessary work.
(Which is not the best news; the best news would be an exception right
away.) That's why I think this is only a "pretty good" reason to use
"if len(lst)>0", not a slam dunk.
Carl Banks

Jul 18 '06 #26

P: n/a
tac-tics wrote:
dwelch91 wrote:
>>tac-tics wrote:
>>>I'd say the second one. Empty lists are not false. They are empty. Long
live dedicated boolean data types.

Uh, no, empty lists are False in a boolean context:

http://docs.python.org/lib/truth.html

-Don


Perhaps I should have specified it like this:

>>>>empty_list = []
empty_list is not False

True

I'm well aware that both of these snippets does the same thing. I'm
just spouting my opinion that lists and integers are not tests, and in
an ideal world (Java??? X-) if statements support only boolean types.

DISCLAIMER: I do not promote the use of Java.
You don't promote use of Python either if you deliberately ignore
programming paradigms that have stood the test of time. Under what set
of rules could it make sense to test a list for equality with a Boolean
value (note also that capital "B": the word is based on someone's name).

The

if lst:

metaphor is so well established as a means of guarding statements that
should be executed only when there are elements in the list (or other
container, come to that), promoting any other way to perform the test
will only lead to confusion: there should be one (and preferably only
one) obvious way to do it.

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://holdenweb.blogspot.com
Recent Ramblings http://del.icio.us/steve.holden

Jul 18 '06 #27

P: n/a
In message <Q8******************************@nmt.edu>, Bob Greschke wrote:
I'd go even one step further. Turn it into English (or your favorite
non-computer language):

1. While list, pop.

2. While the length of the list is greater than 0, pop.

Which one makes more sense? Guess which one I like. CPU cycles be
damned.
:)
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.
Jul 18 '06 #28

P: n/a
Carl Banks wrote:
>
Peter Otten wrote:
>Carl Banks wrote:
def process_values(lst):
if not lst:
return
do_expensive_initialization_step()
for item in lst:
do_something_with(item)
do_expensive_finalization_step()
What if you called the function like this:

process_values(x.strip() for x in values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty). Maybe some
subtle bugs introduced. If we're lucky, the finalization step will
throw an exception.

The good news is that the above has a 99 percent chance that it just
works with iterators/generators -- even though the writer may not have
been aware of them/they didn't exist when the code was written...

There's a litmus test I like to use when justifying something with
percentages: I imagine that I'm presenting it to my boss, and ask
myself if I still expect to have a job the next day. :-)
That 99 percent chance is not meant to exempt you from actually verifying
that it works. Docstrings can do wonders here

def process_values(lst):
"Foos all bars in a list"

vs

def process_values(lst):
"Foos all bars in an iterable"

If in doubt, pass a list (-comprehension) as there are other ways of quiet
failure (the function may loop twice over its argument).
Yes, I agree with you, it most cases I expect merely unnecessary work.
(Which is not the best news; the best news would be an exception right
away.) That's why I think this is only a "pretty good" reason to use
"if len(lst)>0", not a slam dunk.
If process_values() were /aware/ of iterators it would probably be written

def process_values(items):
first_pass = True
for item in items:
if first_pass:
do_expensive_initialization_step()
first_pass = False
do_something_with(item)
if not first_pass:
do_expensive_finalization_step()

or just with an additional

items = list(items)

as its first statement.

Peter
Jul 18 '06 #29

P: n/a
tac-tics wrote:
>>Or even just:

lst = []

;-)


Indeed.

I'd say the second one.
And you'd be plain wrong.
Empty lists are not false.
In a bolean context, empty containers (lists, tuples, dicts, sets etc),
empty strings, integer 0, float 0.0 and None are all false. This is part
of the language specs.

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #30

P: n/a
tac-tics wrote:
dwelch91 wrote:
>>tac-tics wrote:
>>>I'd say the second one. Empty lists are not false. They are empty. Long
live dedicated boolean data types.

Uh, no, empty lists are False in a boolean context:

http://docs.python.org/lib/truth.html

-Don


Perhaps I should have specified it like this:

>>>>empty_list = []
empty_list is not False

True
Physical identity is not structural equality.

I'm well aware that both of these snippets does the same thing. I'm
just spouting my opinion that lists and integers are not tests, and in
an ideal world (Java??? X-)
You naughty troll
if statements support only boolean types.
if statements supports only boolean *expressions*. An expression is
boolean if it's result can be coerced to a boolean value, ie fed to the
bool type's constructor. So your example is wrong wrt/ if statements -
it should read:

empty_list = []
bool(empty_list) is False
=True
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #31

P: n/a
dwelch91 wrote:
PTY wrote:
>Which is better?

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

while lst:
lst.pop()

OR

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

I think the first one is better, but if all you are doing is removing
all the items in the list, this is definitely better:

lst = []
Not if there are other names bound to the same list. You are only
rebinding this name, which does *not* empty the list object. The correct
solution here is

del lst[:]

which will remove all content from the list object,

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #32

P: n/a
PTY wrote:
Bob Greschke wrote:
>><ru***@yahoo.comwrote in message
news:11**********************@m73g2000cwd.google groups.com...
>>>PTY wrote:

Which is better?

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

while lst:
lst.pop()

OR

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

A dozen posts, but nobody has posted the right
answer yet, so I will :-)

It doesn't matter -- use whichever you prefer (*)
This is an angels on the head of a pin issue.

(*) -- If your code is part of an existing body of
code that uses one or the other style consistently,
then you should do the same.

I'd go even one step further. Turn it into English (or your favorite
non-computer language):

1. While list, pop.

2. While the length of the list is greater than 0, pop.

Which one makes more sense? Guess which one I like. CPU cycles be damned.
:)

Bob

It looks like there are two crowds, terse and verbose.
Nope, there are two crowds: those who RTFM, and those who don't.
I thought terse
is perl style and verbose is python style.
s/terse/cryptic/
s/verbose/readable/

Python is much more readable than Java because it's *less* verbose than
Java.
BTW, lst = [] was not what
I was interested in :-)
Nor is it the correct functional equivalent of your code snippet.
I was asking whether it was better style to
use len() or not.
The idiomatic solution is clearly derivable from Python's language
documentation : in a boolean context, empty lists (etc...) eval to
False. FWIW, it's also more generic (you could have an object supporting
pop() but not __len__()), less error-prone, and can allow optimisations
(a container may know that it is empty without computing it's length).
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #33

P: n/a
Carl Banks wrote:
Patrick Maupin wrote:
>>PTY wrote:

>>>It looks like there are two crowds, terse and verbose. I thought terse
is perl style and verbose is python style. BTW, lst = [] was not what
I was interested in :-) I was asking whether it was better style to
use len() or not.

It's not canonical Python to use len() in this case. From PEP 8:

- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

Yes: if not seq:
if seq:

No: if len(seq)
if not len(seq)

The whole reason that a sequence supports testing is exactly for this
scenario. This is not an afterthought -- it's a fundamental design
decision of the language.


That might have made sense when Python and string, list, tuple were the
only sequence types around.

Nowadays, Python has all kinds of spiffy types like numpy arrays,
interators, generators,
etc., for which "empty sequence is false" just
doesn't make sense.
Iterators and generators are *not* sequences types. wrt/ non-builtin
container types, I suggest you re-read section 3.3.1 of the language
references:

"""
__nonzero__( self)
Called to implement truth value testing, and the built-in operation
bool(); should return False or True, or their integer equivalents 0 or
1. When this method is not defined, __len__() is called, if it is
defined (see below). If a class defines neither __len__() nor
__nonzero__(), all its instances are considered true.
"""
http://docs.python.org/ref/customization.html

If Python had been designed with these types in
mind, I'm not sure "empty list is false" would have been part of the
language, let alone recommend practice.
FWIW, this magic method already existed in 1.5.2 :
http://www.python.org/doc/1.5.2p2/re...omization.html
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #34

P: n/a
Lawrence D'Oliveiro wrote:
In message <Q8******************************@nmt.edu>, Bob Greschke wrote:

>>I'd go even one step further. Turn it into English (or your favorite
non-computer language):

1. While list, pop.

2. While the length of the list is greater than 0, pop.

Which one makes more sense? Guess which one I like. CPU cycles be
damned.
:)


One of my rules is, always program like the language actually has a Boolean
type, even if it doesn't.
Python has a boolean type.
That means, never assume that arbitrary values
can be interpreted as true or false,
There's nothing to assume, and nothing arbitrary in it. It's all clearly
defined in whole letters in the language references.
always put in an explicit comparison
if necessary so it's obvious the expression is a Boolean.
The fact that the expression is used in the context of a if statement is
clearly enough to denote a boolean expression. Explicitly testing
against a boolean is uselessly redundant - and doesn't change anything,
since it's always a boolean expression. FWIW, getting rid of theses
"explicit" redundant tests was one of the first things I learned about
programming.
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #35

P: n/a

Bruno Desthuilliers wrote:
Carl Banks wrote:
Patrick Maupin wrote:
>PTY wrote:
It looks like there are two crowds, terse and verbose. I thought terse
is perl style and verbose is python style. BTW, lst = [] was not what
I was interested in :-) I was asking whether it was better style to
use len() or not.

It's not canonical Python to use len() in this case. From PEP 8:

- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

Yes: if not seq:
if seq:

No: if len(seq)
if not len(seq)

The whole reason that a sequence supports testing is exactly for this
scenario. This is not an afterthought -- it's a fundamental design
decision of the language.

That might have made sense when Python and string, list, tuple were the
only sequence types around.

Nowadays, Python has all kinds of spiffy types like numpy arrays,
interators, generators,
etc., for which "empty sequence is false" just
doesn't make sense.

Iterators and generators are *not* sequences types. wrt/ non-builtin
container types, I suggest you re-read section 3.3.1 of the language
references:
I'm aware of the semantics, thank you, and that they're as advertised
in the docs. It doesn't matter whether you call it a sequence or not.
Iterables, lists, arrays, and whatever else have overlapping uses, but
bool(obj) behaves differently for different types, making it unsuitable
for writing generic functions that might use some other types that
aren't vanilla list, tuple, and string.

All I'm saying is, knowing there's lots of roughly-list-like types that
don't consider "empty" to be "false", and that there's not reasonable
way to get consistent behavior as to what the boolean value of such
types is, it's possible that these types wouldn't even have a boolean
value and any tests for emptiness would have to be explicit.

But who knows? Maybe this is just one case where brevity trumps
everything else.
Carl BAnks

Jul 18 '06 #36

P: n/a
Carl Banks wrote:
Bruno Desthuilliers wrote:
>>Carl Banks wrote:
>>>Patrick Maupin wrote:
PTY wrote:

>It looks like there are two crowds, terse and verbose. I thought terse
>is perl style and verbose is python style. BTW, lst = [] was not what
>I was interested in :-) I was asking whether it was better style to
>use len() or not.

It's not canonical Python to use len() in this case. From PEP 8:

- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

Yes: if not seq:
if seq:

No: if len(seq)
if not len(seq)

The whole reason that a sequence supports testing is exactly for this
scenario. This is not an afterthought -- it's a fundamental design
decision of the language.
That might have made sense when Python and string, list, tuple were the
only sequence types around.

Nowadays, Python has all kinds of spiffy types like numpy arrays,
interators, generators,
etc., for which "empty sequence is false" just
doesn't make sense.

Iterators and generators are *not* sequences types. wrt/ non-builtin
container types, I suggest you re-read section 3.3.1 of the language
references:


I'm aware of the semantics, thank you, and that they're as advertised
in the docs. It doesn't matter whether you call it a sequence or not.
Your opinion - not mine. Sequences are iterable, but not all iterables
are sequences.
Iterables, lists, arrays, and whatever else have overlapping uses, but
bool(obj) behaves differently for different types,
bool(obj) will mainly look for __len__(), then for __nonzero__(), then
return True. You can only call len(obj) on objects implementing
__len__(), so relying on (implicit) 'bool(obj)' is clearly more generic
than 'len(obj) 0'. Also, since bool(obj) will try to call
obj.__len__(), explicitely calling len(obj) doesn't make any difference.
making it unsuitable
for writing generic functions that might use some other types that
aren't vanilla list, tuple, and string.
Using an explicit test against the length of an iterator or generator
*won't* work anyway, since iterators and generators are unsized. And
that's one of the reasons why the difference between sequences and
iterables does matter.

And if you want your own sequence types to be well-behaved sequence
types, just take time to implement the needed protocol(s), including
__len__().

For short: either your function expects an iterable or it expects a
sequence. If it expects an iterable, treat it as an iterable wether it
happens to be a sequence or not. If it expects a sequence, it obviously
won't work with a non-sequence.
All I'm saying is, knowing there's lots of roughly-list-like
"roughly-list-like" ?
types that
don't consider "empty" to be "false",
This means they are unsized (ie don't implement __len__).
and that there's not reasonable
way to get consistent behavior as to what the boolean value of such
types is,
I don't know what "types" you are talkink about, but seems like either
they're not designed to be treated as sequences or they are badly
implemented. In the first case, you're not using them correctly. In the
second case, send a bug report to the authors.
it's possible that these types wouldn't even have a boolean
value and any tests for emptiness would have to be explicit.
Yes ? How ? Calling len() on them ?-)
(hint: reread how len() and bool() works wrt/ __len__())

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #37

P: n/a

Bruno Desthuilliers wrote:
Carl Banks wrote:
Iterables, lists, arrays, and whatever else have overlapping uses, but
bool(obj) behaves differently for different types,

bool(obj) will mainly look for __len__(), then for __nonzero__(), then
return True. You can only call len(obj) on objects implementing
__len__(), so relying on (implicit) 'bool(obj)' is clearly more generic
than 'len(obj) 0'. Also, since bool(obj) will try to call
obj.__len__(), explicitely calling len(obj) doesn't make any difference.
I'm well aware of Python's semantics, and it's irrelvant to my
argument. What low level operations it does do not concern me. I'm
concerned about what equats to "truth". For generators and such, it's
"always". For lists, it's "empty". For numpy arrays, there is no
truth value.

For the purpose of writing generic functions, this is inconsistent.
And, IMO, it's possible that the language would have been designed
differently (by not letting them have any truth value) if they'd been
around in the beginning.

And if you want your own sequence types to be well-behaved sequence
types, just take time to implement the needed protocol(s), including
__len__().
You know very well that not all iterables can know their length ahead
of time.

For short: either your function expects an iterable or it expects a
sequence. If it expects an iterable, treat it as an iterable wether it
happens to be a sequence or not.
1. This is fine in a perfect world where all code clearly conforms to
expectation. Problem is, a lot doesn't. I think it's quite easy to
accidentally check something intended as an iterable for emptiness.
And, as I've explained in other posts, this can lead to subtle bugs.
Whereas if you check for emptiness using len, it throws an exception
right away, no bugs. It's safer to use len. (Safest of all is not to
check for emptiness at all.)

2. This logic doesn't work for numpy arrays. In a lot of cases, a
function expecting a list works perfectly fine if you pass it a numpy
arrray. But, if you were to write such a function with "if lst"
instead of "if len(lst)>0", it's guaranteed to fail. The function is
more generic with the len test.

it's possible that these types wouldn't even have a boolean
value and any tests for emptiness would have to be explicit.

Yes ? How ? Calling len() on them ?-)
(hint: reread how len() and bool() works wrt/ __len__())
Yes. If the language had been designed such that a list had no
nb_nonzero slot, you'd have to check for emptiness with len. Perhaps
you're missing some of the nuances of English verb tenses (don't feel
too bad, it's hard). I'm not arguing "what is", I'm arguing what
"could have been if circumstances were different". If the language
were designed differently, then the rules would be different.
Carl Banks

Jul 18 '06 #38

P: n/a
Carl Banks wrote:
Bruno Desthuilliers wrote:
>>Carl Banks wrote:
>>>Iterables, lists, arrays, and whatever else have overlapping uses, but
bool(obj) behaves differently for different types,

bool(obj) will mainly look for __len__(), then for __nonzero__(), then
return True. You can only call len(obj) on objects implementing
__len__(), so relying on (implicit) 'bool(obj)' is clearly more generic
than 'len(obj) 0'. Also, since bool(obj) will try to call
obj.__len__(), explicitely calling len(obj) doesn't make any difference.


I'm well aware of Python's semantics, and it's irrelvant to my
argument. What low level operations it does do not concern me. I'm
concerned about what equats to "truth". For generators and such, it's
"always".
"generators and such" are not sequences.
For lists, it's "empty". For numpy arrays, there is no
truth value.
Then treat numpy arrays as iterables (which they are), not as sequences.
For the purpose of writing generic functions, this is inconsistent.
Not if you choose to design your function to work on iterables (which
means you don't use len() at all to test for emptiness).
And, IMO, it's possible that the language would have been designed
differently (by not letting them have any truth value) if they'd been
around in the beginning.
>>And if you want your own sequence types to be well-behaved sequence
types, just take time to implement the needed protocol(s), including
__len__().


You know very well that not all iterables can know their length ahead
of time.
Yes, but I was talking about *sequences*, not iterables.
>
>>For short: either your function expects an iterable or it expects a
sequence. If it expects an iterable, treat it as an iterable wether it
happens to be a sequence or not.


1. This is fine in a perfect world where all code clearly conforms to
expectation. Problem is, a lot doesn't. I think it's quite easy to
accidentally check something intended as an iterable for emptiness.
Testing a random iterable for emptiness can not be done by testing it's
length.
And, as I've explained in other posts, this can lead to subtle bugs.
If you clearly documents what your function expects (ie sequence or
iterable), then I don't see any subtleties involved...
Whereas if you check for emptiness using len, it throws an exception
right away, no bugs. It's safer to use len.
It's safer to decide which protocol is expected, ie iterable or
sequence, and then write the code according to this expectation.
(Safest of all is not to
check for emptiness at all.)
Unless you clearly expect a sequence and have to branch according to
it's state (ie empty/not empty).
2. This logic doesn't work for numpy arrays. In a lot of cases, a
function expecting a list works perfectly fine if you pass it a numpy
arrray. But, if you were to write such a function with "if lst"
instead of "if len(lst)>0", it's guaranteed to fail. The function is
more generic with the len test.
If the function is to work with non-sequence types, write it so it can
work with non-sequence types.
>>>it's possible that these types wouldn't even have a boolean
value and any tests for emptiness would have to be explicit.

Yes ? How ? Calling len() on them ?-)
(hint: reread how len() and bool() works wrt/ __len__())


Yes. If the language had been designed such that a list had no
nb_nonzero slot,
>>[].__nonzero__
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'list' object has no attribute '__nonzero__'

you'd have to check for emptiness with len.
bool(obj) *do* the test on __len__() if __nonzero__() is not implemented.
Perhaps
you're missing some of the nuances of English verb tenses (don't feel
too bad, it's hard). I'm not arguing "what is", I'm arguing what
"could have been if circumstances were different". If the language
were designed differently, then the rules would be different.
Totally true - and totally irrelevant IMHO.

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 18 '06 #39

P: n/a

Carl Banks wrote:
Here's another reason not to use "if lst". Say you have a function
that looks like this:

def process_values(lst):
if not lst:
return
do_expensive_initialization_step()
for item in lst:
do_something_with(item)
do_expensive_finalization_step()

That works, right? No problem, right?

What if you called the function like this:

process_values(x.strip() for x in values_lst)

Oops, now we've just gone through an expensive initialization and
finalization for nothing (since values_lst was empty). Maybe some
subtle bugs introduced. If we're lucky, the finalization step will
throw an exception.

If we had used "if len(list)>0", we'd have gotten a nice exception
telling us that a generator is not welcome. Then we'd have changed the
argument to a list comprehension, or better, changed the function to
work for any iterator.
The argument that one should always use len() to test whether sequences
are empty or not simply because the use of len() gives a free "is it a
sequence?" type-check is not apt to be well received by an audience
which rejects explicit type-checking in general.

The specific example given, where a lack of a len() works correctly,
except for using more execution time on generators than on lists, is
not apt to be well received by an audience which rejects premature
optimizations in general, and which believes that if you don't notice a
speed problem, it doesn't exist, and if you _do_ notice a speed
problem, the profiler is the best tool to attack it with.

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; and I daresay most members of that audience, if
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.

I'm not saying that _everybody_ on this list is in any of these
audiences, but I would suspect that all the people arguing with you are
:)

Regards,
Pat

Jul 18 '06 #40

P: n/a
Bruno Desthuilliers <on***@xiludom.groschrieb:
Carl Banks wrote:
>Bruno Desthuilliers wrote:

I'm well aware of Python's semantics, and it's irrelvant to my
argument.
[...]
>If the language
were designed differently, then the rules would be different.

Totally true - and totally irrelevant IMHO.
I strongly advise not to treat each others thoughts as irrelevant.
Assuming the opposite is a base of every public dicussion forum.
I assume here is a flaw in Python. To explain this, I'd like to
make Bruno's point clearer. As usually, code tells more then
thousand words (an vice versa :-)).

Suppose you have two functions which somehow depend on the emptyness
of a sequence. This is a stupid example, but it demonstrates at
least the two proposed programming styles:

------------------------------------------------------
>>def test1(x):
.... if x:
.... print "Non-Empty"
.... else:
.... print "Empty"
....
>>def test2(x):
.... if len(x) 0:
.... print "Non-Empty"
.... else:
.... print "Empty"
------------------------------------------------------

Bruno pointed out a subtle difference in the behaviour of those
functions:

------------------------------------------------------
>>a = []
test1(a)
Empty
>>test1(iter(a))
Non-Empty
>>test2(a)
Empty
>>test2(iter(a))
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in test2
TypeError: len() of unsized object
------------------------------------------------------
While test1() returns a wrong/random result when called with an
iterator, the test2() function breaks when beeing called wrongly.

So if you accidently call test1() with an iterator, the program
will do something unintended, and the source of that bug will be
hard to find. So Bruno is IMHO right in calling that the source
of a suptle bug.

However, if you call test2() with an iterator, the program will
cleanly break early enough with an exception. That is generally
wanted in Python. You can see this all over the language, e.g.
with dictionaries:

------------------------------------------------------
>>d = { 'one': 1 }
print d['one']
1
>>print d['two']
Traceback (most recent call last):
File "<stdin>", line 1, in ?
KeyError: 'two'
------------------------------------------------------

Python could have been designed to return None when d['two'] has been
called, as some other (bad) programming languages would. This would
mean that the problem will occur later in the program, making it easy
to produce a subtle bug. It would be some effort to figure out the
real cause, i.e. that d had no entry for 'two'.

Luckily, Python throws an exception (KeyError) just at the original
place where the initial mistake occured. If you *want* to get None in
case of a missing key, you'll have to say this explicitly:

------------------------------------------------------
>>print d.get('two', None)
None
------------------------------------------------------

So maybe "bool()" should also break with an exception if an object
has neither a __nonzero__ nor a __len__ method, instead of defaulting
to True. Or a more strict variant of bool() called nonempty() should
exist.

Iterators don't have a meaningful Boolean representation, because
phrases like "is zero" or "is empty" don't make sense for them. So
instead of answering "false", an iterator should throw an exception
when beeing asked whether he's empty.

If a function expects an object to have a certain protocol (e.g.
sequence), and the given object doesn't support that protocol,
an exception should be raised. This usually happens automatically
when the function calls a non-existing method, and it plays very
well with duck typing.

test2() behaves that way, but test1() doesn't. The reason is a
sluttery of Python. Python should handle that problem as strict
as it handles a missing key in a dictionary. Unfortunately, it
doesn't.

I don't agree with Bruno that it's more natural to write
if len(a) 0:
...
instead of
if a:
...

But I think that this is a necessary kludge you need to write
clean code. Otherwise you risk to create subtle bugs. This advise,
however, only applies when your function wants a sequence, because
only in that can expect "len(a)" to work.

I also agree with Carl that "if len(a) 0" is less universal than
"if a", because the latter also works with container-like objects
that have a concept of emptiness, but not of length.

However, this case is less likely to happen than shooting yourself
in the foot by passing accidently an iterator to the function
without getting an exception. I think, this flaw in Python is deep
enough to justify the "len() 0" kludge.
IMHO, that flaw of Python should be documented in a PEP as it violates
Python's priciple of beeing explicit. It also harms duck typing.
Greets,

Volker

--
Volker Grabsch
---<<(())>>---
Administrator
NotJustHosting GbR
Jul 18 '06 #41

P: n/a
In article <11*********************@75g2000cwc.googlegroups.c om>,
"Carl Banks" <pa************@gmail.comwrote:
1. This is fine in a perfect world where all code clearly conforms to
expectation. Problem is, a lot doesn't. I think it's quite easy to
accidentally check something intended as an iterable for emptiness.
And, as I've explained in other posts, this can lead to subtle bugs.
Whereas if you check for emptiness using len, it throws an exception
right away, no bugs. It's safer to use len. (Safest of all is not to
check for emptiness at all.)
Yes, it's clearly more direct to catch IndexError, than to
try to anticipate it.

Donn Cave, do**@u.washington.edu
Jul 18 '06 #42

P: n/a
Bruno Desthuilliers <on***@xiludom.groschrieb:
PTY wrote:
>I was asking whether it was better style to
use len() or not.

FWIW, it's also more generic (you could have an object supporting
pop() but not __len__()), less error-prone,
While I agree with all other points, I don't think that it's less
error-prone. See my other posting where I worked out this a flaw
of Python.
Greets,

Volker

--
Volker Grabsch
---<<(())>>---
Administrator
NotJustHosting GbR
Jul 18 '06 #43

P: n/a
Patrick Maupin wrote:
The argument that one should always use len() to test whether sequences
are empty or not simply because the use of len() gives a free "is it a
sequence?" type-check is not apt to be well received by an audience
which rejects explicit type-checking in general.
No explicit type check. More of an implicit behaviour check. OK,
wise guys are not apt to be well received by ... well, any audience.
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; and I daresay most members of that audience, if
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.
Premature generalization: the new 'premature optimization'.

Daniel
Jul 18 '06 #44

P: n/a

Daniel Dittmar wrote:
>
Premature generalization: the new 'premature optimization'.
Premature specialization: the new 'static typing'.

-- Pat

Jul 18 '06 #45

P: n/a
"Carl Banks" <pa************@gmail.comwrites:
Patrick Maupin wrote:
>PTY wrote:
It looks like there are two crowds, terse and verbose. I thought terse
is perl style and verbose is python style. BTW, lst = [] was not what
I was interested in :-) I was asking whether it was better style to
use len() or not.

It's not canonical Python to use len() in this case. From PEP 8:

- For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

Yes: if not seq:
if seq:

No: if len(seq)
if not len(seq)

The whole reason that a sequence supports testing is exactly for this
scenario. This is not an afterthought -- it's a fundamental design
decision of the language.

That might have made sense when Python and string, list, tuple were the
only sequence types around.

Nowadays, Python has all kinds of spiffy types like numpy arrays,
interators, generators, etc., for which "empty sequence is false" just
doesn't make sense. If Python had been designed with these types in
mind, I'm not sure "empty list is false" would have been part of the
language, let alone recommend practice.
Bruno's already mentioned that iterators and generators aren't
sequences. Numpy arrays act like the other sequence types:
>>a = numpy.array([])
a
array([], dtype=int64)
>>len(a)
0
>>bool(a)
False

(0-dimensional numpy arrays are pathological anyways)

--
|>|\/|<
/--------------------------------------------------------------------------\
|David M. Cooke
|cookedm(at)physics(dot)mcmaster(dot)ca
Jul 18 '06 #46

P: n/a
co**********@physics.mcmaster.ca (David M. Cooke) writes:
>
Bruno's already mentioned that iterators and generators aren't
sequences. Numpy arrays act like the other sequence types:
>>>a = numpy.array([])
a
array([], dtype=int64)
>>>len(a)
0
>>>bool(a)
False

(0-dimensional numpy arrays are pathological anyways)
*cough* as a Numpy developer I should know better. Numpy arrays that
have more than one element don't work in a boolean context:
>>a = numpy.array([1,2])
bool(a)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

The reason for this is that it really was a common source of errors,
because of the rich comparision semantics used. If a and b are numpy
arrays, 'a == b' is an array of booleans.

Numpy arrays of one element act like scalars in boolean contexts:
>>a = numpy.array([0])
bool(a)
False
>>a = numpy.array([1])
bool(a)
True

(this is partly because we define a comphensive hierarchy of scalar
types to match those available in C).

--
|>|\/|<
/--------------------------------------------------------------------------\
|David M. Cooke
|cookedm(at)physics(dot)mcmaster(dot)ca
Jul 18 '06 #47

P: n/a
Volker Grabsch wrote:
IMHO, that flaw of Python should be documented in a PEP as it violates
Python's priciple of beeing explicit. It also harms duck typing.
I think this may be good food for Python 3.0, the are removing
undefined comparisons too (>), etc.

bye,
bearophile

Jul 18 '06 #48

P: n/a
Volker Grabsch a écrit :
Bruno Desthuilliers <on***@xiludom.groschrieb:
>>Carl Banks wrote:
>>>Bruno Desthuilliers wrote:

I'm well aware of Python's semantics, and it's irrelvant to my
argument.

[...]
>>>If the language
were designed differently, then the rules would be different.

Totally true - and totally irrelevant IMHO.


I strongly advise not to treat each others thoughts as irrelevant.
Assuming the opposite is a base of every public dicussion forum.
"Irrelevant" may not be the best expression of my thought here - it's
just that Carl's assertion is kind of a tautology and doesn't add
anything to the discussion. If Python had been designed as statically
typed (with declarative typing), the rules would be different. Yeah,
great. And now ?
I assume here is a flaw in Python. To explain this, I'd like to
make Bruno's point
Actually Carl's point, not mine.
clearer. As usually, code tells more then
thousand words (an vice versa :-)).

Suppose you have two functions which somehow depend on the emptyness
of a sequence. This is a stupid example, but it demonstrates at
least the two proposed programming styles:

------------------------------------------------------
>>>>def test1(x):

... if x:
... print "Non-Empty"
... else:
... print "Empty"
...
>>>>def test2(x):

... if len(x) 0:
... print "Non-Empty"
... else:
... print "Empty"
------------------------------------------------------

Bruno
Carl
pointed out a subtle difference in the behaviour of those
functions:

------------------------------------------------------
>>>>a = []
test1(a)

Empty
>>>>test1(iter(a))

Non-Empty
>>>>test2(a)

Empty
>>>>test2(iter(a))

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in test2
TypeError: len() of unsized object
------------------------------------------------------
While test1() returns a wrong/random result when called with an
iterator, the test2() function breaks when beeing called wrongly.
Have you tried these functions with a numpy array ?
So if you accidently call test1() with an iterator, the program
will do something unintended, and the source of that bug will be
hard to find. So Bruno is IMHO right in calling that the source
of a suptle bug.
Actually it's Carl who makes that point - MHO being that it's a
programmer error to call a function with a param of the wrong type.
However, if you call test2() with an iterator, the program will
cleanly break early enough with an exception. That is generally
wanted in Python. You can see this all over the language, e.g.
with dictionaries:

------------------------------------------------------
>>>>d = { 'one': 1 }
print d['one']

1
>>>>print d['two']

Traceback (most recent call last):
File "<stdin>", line 1, in ?
KeyError: 'two'
------------------------------------------------------

Python could have been designed to return None when d['two'] has been
called, as some other (bad) programming languages would. This would
mean that the problem will occur later in the program, making it easy
to produce a subtle bug. It would be some effort to figure out the
real cause, i.e. that d had no entry for 'two'.
I don't think the comparison is right. The equivalent situation would be
to have a function trying to access d['two'] on a dict-like type that
would return a default value instead of raising a KeyError.
Luckily, Python throws an exception (KeyError) just at the original
place where the initial mistake occured. If you *want* to get None in
case of a missing key, you'll have to say this explicitly:

------------------------------------------------------
>>>>print d.get('two', None)

None
------------------------------------------------------

So maybe "bool()" should also break with an exception if an object
has neither a __nonzero__ nor a __len__ method, instead of defaulting
to True.
FWIW, Carl's main example is with numpy arrays, that have *both* methods
- __nonzero__ raising an expression.
Or a more strict variant of bool() called nonempty() should
exist.

Iterators don't have a meaningful Boolean representation,
because
phrases like "is zero" or "is empty" don't make sense for them.
If so, almost no type actually has a "meaningfull" boolean value. I'd
rather say that iterators being unsized, the mere concept of an "empty"
iterator has no meaning.
So
instead of answering "false", an iterator should throw an exception
when beeing asked whether he's empty.

If a function expects an object to have a certain protocol (e.g.
sequence), and the given object doesn't support that protocol,
an exception should be raised.
So you advocate static typing ? Note that numpy arrays actually have
both __len__ and __nonzero__ defined, the second being defined to
forgive boolean coercion...
This usually happens automatically
when the function calls a non-existing method, and it plays very
well with duck typing.

test2() behaves that way, but test1() doesn't. The reason is a
sluttery of Python. Python should handle that problem as strict
as it handles a missing key in a dictionary. Unfortunately, it
doesn't.
Then proceed to write a PEP proposing that evaluating the truth value of
an iterator would raise a TypeError. Just like numpy arrays do - as a
decision of it's authors.
I don't agree with Bruno
s/bruno/Carl/
that it's more natural to write
if len(a) 0:
...
instead of
if a:
...

But I think that this is a necessary kludge you need to write
clean code. Otherwise you risk to create subtle bugs.
s/you risk to create/careless programmers will have to face/

And FWIW, this is clearly not the opinion of numpy authors, who state
that having len 0 doesn't means the array is "not empty"...
This advise,
however, only applies when your function wants a sequence, because
only in that can expect "len(a)" to work.
Since sequence types are defined as having a False value when empty,
this test is redondant *and* "will create subtle bugs" when applied to a
numpy array.
I also agree with Carl that "if len(a) 0" is less universal than
"if a", because the latter also works with container-like objects
that have a concept of emptiness,
s/emptiness/boolean value/
but not of length.
However, this case is less likely to happen than shooting yourself
in the foot by passing accidently an iterator to the function
without getting an exception. I think, this flaw in Python is deep
enough to justify the "len() 0" kludge.
It surely justify some thinking on the boolean value of iterators. Since
the common idiom for testing non-None objects is an explicit identity
test against None - which makes sens since empty sequences and zero
numerics eval to False in a boolean context - the less inappropriate
solution would be to have iterators implementing __nonzero__ like numpy
arrays do.
>
IMHO, that flaw of Python should be documented in a PEP as it violates
Python's priciple of beeing explicit.
Here again, while I agree that there's room for improvement, I don't
agree on this behaviour being a "flaw" - "minor wart" would better
describe the situation IMHO.
Jul 18 '06 #49

P: n/a
Volker Grabsch a écrit :
Bruno Desthuilliers <on***@xiludom.groschrieb:
>>PTY wrote:
>>>I was asking whether it was better style to
use len() or not.

FWIW, it's also more generic (you could have an object supporting
pop() but not __len__()), less error-prone,


While I agree with all other points, I don't think that it's less
error-prone.
There are less risk of a typo with "if a:" than with "if len(a) 0".
See my other posting where I worked out this a flaw
of Python.
See my answer where I don't agree on this being a "flaw" - a "minor
wart" at most !-)
Jul 18 '06 #50

86 Replies

This discussion thread is closed

Replies have been disabled for this discussion.