which is more 'pythonic' / 'better' ?

G

gabor

hi,

there are 2 versions of a simple code.
which is preferred?


===
if len(line) >= (n+1):
text = line[n]
else:
text = 'nothing'
===


===
try:
text = line[n]
except IndexError:
text = 'nothing'
===


which is the one you would use?

thanks,
gabor
 
W

Will McGugan

gabor said:
hi,

there are 2 versions of a simple code.
which is preferred?


===
if len(line) >= (n+1):
text = line[n]
else:
text = 'nothing'
===


===
try:
text = line[n]
except IndexError:
text = 'nothing'
===


which is the one you would use?

I would actualy use the following for this particular case..

text = line[n:n+1] or 'nothing'

But in general I think it is best to use exceptions like that only where
you expect the code to _not_ throw the exception the majority of
times. Otherwise the simple condition is better. Although I expect there
is not much difference either way..


Will McGugan
 
S

Steven D'Aprano

hi,

there are 2 versions of a simple code.
which is preferred?


===
if len(line) >= (n+1):
text = line[n]
else:
text = 'nothing'
===


===
try:
text = line[n]
except IndexError:
text = 'nothing'
===


which is the one you would use?

Personally, I would use either. You say po-ta-to, I say pot-at-o.

try...except... blocks are quick to set up, but slow to catch the
exception. If you expect that most of your attempts will succeed, then the
try block will usually be faster than testing the length of the list
each time.

But if you expect that the attempts to write the line will fail more
frequently, then testing will be quicker.

You will need to do your own testing before hand to find the exact
cut-off, and expect that cut-off to vary according to the Python
implementation and version. But a rough rule of thumb is, if you expect
your code to fail more often than succeed, then test first, otherwise
catch an exception.
 
P

Pierre Barbier de Reuille

Will McGugan a écrit :
gabor said:
hi,

there are 2 versions of a simple code.
which is preferred?


===
if len(line) >= (n+1):
text = line[n]
else:
text = 'nothing'
===


===
try:
text = line[n]
except IndexError:
text = 'nothing'
===


which is the one you would use?


I would actualy use the following for this particular case..

text = line[n:n+1] or 'nothing'

.... and you would get either a list of one element or a string ...
I think you wanted to write :

text = (line[n:n+1] or ['nothing'])[0]

However, I wouldn't use that because it is hard to read ... you have to
know Python in great detail to know that:

1 - is the expressions "line[i:j]", i and j are replaced with
"len(line)" if they are greater than "len(line)"
2 - so if n > len(line), then line[n:n+1]" == len[len(line):len(line)]
== []
(it is not evident that line[n:n+1] can give an empty list ...)
3 - empty list evaluate to "False"
4 - the "or" operator returns the first argument evaluating to "true"

So, in the end, you use 3 side-effects of Python in the same small
expression ... (1, 2 and 4)
But in general I think it is best to use exceptions like that only where
you expect the code to _not_ throw the exception the majority of times.
Otherwise the simple condition is better. Although I expect there is not
much difference either way..


Will McGugan

What I would do is the following:
- if this happen in a loop, I would, if possible, remove any test and
catch the exception outside the loop !
- otherwise, I would go for the test, as it is more straitforward to read.

Pierre
 
W

Will McGugan

Pierre said:
I would actualy use the following for this particular case..

text = line[n:n+1] or 'nothing'


... and you would get either a list of one element or a string ...
I think you wanted to write :

text = (line[n:n+1] or ['nothing'])[0]

I was assuming that 'line' would be a string, not a list. Seems more
likely give the name and context.

Will McGugan
 
S

Steven Bethard

Steven said:
try...except... blocks are quick to set up, but slow to catch the
exception. If you expect that most of your attempts will succeed, then the
try block will usually be faster than testing the length of the list
each time.

But if you expect that the attempts to write the line will fail more
frequently, then testing will be quicker.

You will need to do your own testing before hand to find the exact
cut-off, and expect that cut-off to vary according to the Python
implementation and version. But a rough rule of thumb is, if you expect
your code to fail more often than succeed, then test first, otherwise
catch an exception.

FWIW, these are almost exactly my criteria too. Exceptions are for
"exceptional" conditions, that is, things that you expect to happen
infrequently[1]. So if I think the code is going to fail frequently, I
test the condition, but if I think it won't, I use exceptions.

STeVe

[1] Note though that what is "infrequent" in Python might be still
considered "frequent" in other languages. For example, Java's iterators
check the result of a .hasNext() method before each .next() call, while
Python's iterators assume the .next() call will succeed, and simply test
for the "exceptional" condition of a StopIteration exception.
 
S

Steve Holden

Will said:
Pierre Barbier de Reuille wrote:

I would actualy use the following for this particular case..

text = line[n:n+1] or 'nothing'


... and you would get either a list of one element or a string ...
I think you wanted to write :

text = (line[n:n+1] or ['nothing'])[0]


I was assuming that 'line' would be a string, not a list. Seems more
likely give the name and context.
I'd say it's much more likely that line is a list of lines, since it
seems improbable that absence of a character should cause a value of
"nothing" to be required.

so-i-say-po-tay-to-ly y'rs - steve
 
M

Michael Hoffman

Steven said:
> Exceptions are for
"exceptional" conditions, that is, things that you expect to happen
infrequently[1]. So if I think the code is going to fail frequently, I
test the condition, but if I think it won't, I use exceptions.

I think there exceptions (no pun intended) to that rule as well. A
classic example is writing to a file. Even if you expect this to be
impossible, it's best to just create it and trap the exception, thereby
avoiding a race condition.
 
W

Will McGugan

Steve said:
I'd say it's much more likely that line is a list of lines, since it
seems improbable that absence of a character should cause a value of
"nothing" to be required.

You may be right. I always use plural nouns for collections. To me
'line' would suggest there was just one of them, so I assumed it was string.


Will McGugan
 
T

Terry Reedy

Will McGugan said:
You may be right. I always use plural nouns for collections.
ditto
To me 'line' would suggest there was just one of them,
so I assumed it was string.

I did too.

for line in file('skljflask.txt',r): # or similar

is a common idiom posted here many times.

Terry J. Reedy
 
G

gabor

Terry said:
I did too.

i'm sorry ;) it was a list of strings...

the code was something like:

for line in open('x.y'):
line = line.split('\t')


a better naming would be better it seems :)

gabor
 
T

Terry Reedy

gabor said:
i'm sorry ;) it was a list of strings...

the code was something like:

for line in open('x.y'):
line = line.split('\t')

a better naming would be better it seems :)

Like 'fields', for a list of fields ;-?

Or your question could have either included the code above or simply
specified what 'line' was. Asking clear questions is a learning process.

Terry J. Reedy
 
K

Ken Seehart

Will said:
gabor said:
hi,

there are 2 versions of a simple code.
which is preferred?


===
if len(line) >= (n+1):
text = line[n]
else:
text = 'nothing'
===


===
try:
text = line[n]
except IndexError:
text = 'nothing'
===


which is the one you would use?


I would actualy use the following for this particular case..

text = line[n:n+1] or 'nothing'

But in general I think it is best to use exceptions like that only where
you expect the code to _not_ throw the exception the majority of times.
Otherwise the simple condition is better. Although I expect there is not
much difference either way..


Will McGugan

Hey are you a perl programmer? That looks perlish to me. A python
programmer would never use "or" that way (even though it works). :)

It's okay, I used to be a perl programmer too. It's nothing to be
ashamed of. :)

- Ken
 
P

Peter Hansen

Ken said:
Will said:
I would actualy use the following for this particular case..

text = line[n:n+1] or 'nothing'
Hey are you a perl programmer? That looks perlish to me. A python
programmer would never use "or" that way (even though it works). :)

I don't think that's at all true. The pattern "somevalue or default" is
an accepted idiom for returning a default value when "somevalue" is
False, often used inside __init__ methods to set up attributes. A most
common case is like this (inside a class obviously):

def meth(self, things=None):
self.things = things or []

(The reason you don't just use a keyword argument of "things=[]" should
be obvious to all but newbies, and they'll learn a lot by researching
why so I won't say here. ;-) )

The alternative is fine too, but insisting on it would be pedantic, and
if you have more than one of these it is definitely less readable (and,
therefore, not Pythonic):

def meth(self, things=None):
if things:
self.things = things
else:
self.things = []
It's okay, I used to be a perl programmer too. It's nothing to be
ashamed of. :)

Ah, now I would disagree with that as well! ;-)

-Peter
 
S

Steven Bethard

Peter said:
def meth(self, things=None):
self.things = things or []
[snip]

The alternative is fine too, but insisting on it would be pedantic, and
if you have more than one of these it is definitely less readable (and,
therefore, not Pythonic):

def meth(self, things=None):
if things:
self.things = things
else:
self.things = []

Probably worth pointing out that there is at least one more alternative:

def meth(self, things=None):
if things is None:
things = []
self.things = things

I usually opt for this one, mainly because "things or []" makes me
nervous -- it has different behavior if the user passes in an empty list:

py> class Things1(object):
.... def __init__(self, things=None):
.... if things is None:
.... things = []
.... self.things = things
....
py> class Things2(object):
.... def __init__(self, things=None):
.... self.things = things or []
....
py> lst = []
py> thing = Things1(lst)
py> thing.things.append(100)
py> thing.things, lst
([100], [100])
py> lst = []
py> thing = Things2(lst)
py> thing.things.append(100)
py> thing.things, lst
([100], [])

That said, I do use "and" and "or" occasionally when I'm sure I don't
have to worry about complications like the above. I've probably even
used them in an assignment statement. ;)

STeVe
 
J

Jorgen Grahn

hi,

there are 2 versions of a simple code.
which is preferred?

I don't know. Who cares?
===
try:
text = line[n]
except IndexError:
text = 'nothing'
===


which is the one you would use?

The 'try' version. But I'd also ask myself how I could end up in a state where
this part of the code is asked to find a string that doesn't exist, and if I
really want it to keep running, with a made-up value.

/Jorgen
 

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

Staff online

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top