converting a nested try/except statement into try/except/else

Discussion in 'Python' started by John Salerno, Aug 9, 2006.

  1. John Salerno

    John Salerno Guest

    I'm starting out with this:

    try:
    if int(text) > 0:
    return True
    else:
    self.error_message()
    return False
    except ValueError:
    self.error_message()
    return False

    I rewrote it as this:

    try:
    int(text)
    except ValueError:
    self.error_message()
    return False
    else:
    return True

    I think it's much cleaner, but obviously I lost the test in the original
    if statement.

    So my question is, can I still retain this second structure and still
    test for > 0, but not have any extra nesting?

    Thanks.
     
    John Salerno, Aug 9, 2006
    #1
    1. Advertising

  2. John Salerno

    Simon Forman Guest

    John Salerno wrote:
    > I'm starting out with this:
    >
    > try:
    > if int(text) > 0:
    > return True
    > else:
    > self.error_message()
    > return False
    > except ValueError:
    > self.error_message()
    > return False
    >
    > I rewrote it as this:
    >
    > try:
    > int(text)
    > except ValueError:
    > self.error_message()
    > return False
    > else:
    > return True
    >
    > I think it's much cleaner, but obviously I lost the test in the original
    > if statement.
    >
    > So my question is, can I still retain this second structure and still
    > test for > 0, but not have any extra nesting?
    >
    > Thanks.


    What about the version I gave you 8 days ago? ;-)

    http://groups.google.ca/group/comp.lang.python/msg/a80fcd8932b0733a

    It's clean, does the job, and doesn't have any extra nesting.

    Peace,
    ~Simon
     
    Simon Forman, Aug 9, 2006
    #2
    1. Advertising

  3. John Salerno a écrit :
    > I'm starting out with this:
    >
    > try:
    > if int(text) > 0:
    > return True
    > else:
    > self.error_message()
    > return False
    > except ValueError:
    > self.error_message()
    > return False
    >
    > I rewrote it as this:
    >
    > try:
    > int(text)
    > except ValueError:
    > self.error_message()
    > return False
    > else:
    > return True
    >
    > I think it's much cleaner, but obviously I lost the test in the original
    > if statement.
    >
    > So my question is, can I still retain this second structure and still
    > test for > 0, but not have any extra nesting?


    solution 1:

    def wrong():
    raise ValueError

    try:
    int(text) > 0 or wrong()
    except ValueError:
    self.error_message()
    return False
    else:
    return True

    But that's being-too-clever imho...

    solution 2:

    def error_message():
    self.error_message()
    return False

    try:
    return int(text) > 0 or error_message()
    except ValueError:
    return error_message()
     
    Bruno Desthuilliers, Aug 9, 2006
    #3
  4. Bruno Desthuilliers a écrit :
    > John Salerno a écrit :
    >

    (snip)

    or of course the dead simple:

    try:
    if int(text) <= 0: raise ValueError
    except ValueError:
    self.error_message()
    return False
    else:
    return True


    BTW, you really should have a look at FormEncode...
     
    Bruno Desthuilliers, Aug 9, 2006
    #4
  5. John Salerno

    John Salerno Guest

    Bruno Desthuilliers wrote:

    > try:
    > if int(text) <= 0: raise ValueError
    > except ValueError:
    > self.error_message()
    > return False
    > else:
    > return True


    Nice! Thanks!
     
    John Salerno, Aug 10, 2006
    #5
  6. John Salerno

    John Salerno Guest

    Simon Forman wrote:

    > What about the version I gave you 8 days ago? ;-)
    >
    > http://groups.google.ca/group/comp.lang.python/msg/a80fcd8932b0733a
    >
    > It's clean, does the job, and doesn't have any extra nesting.
    >
    > Peace,
    > ~Simon
    >


    I remember that version, but I found it a little hard to follow. It
    seems like the kind of code that if I look at it again in another month
    or so, I'll have to trace through it again to figure out what's what.

    But I think it was your code that made me think of using an else
    statement in the first place! :)
     
    John Salerno, Aug 10, 2006
    #6
  7. John Salerno

    John Salerno Guest

    Bruno Desthuilliers wrote:

    > try:
    > if int(text) <= 0: raise ValueError


    Hmm, I'm actually not so sure about this line now. It doesn't seem right
    to raise a ValueError when the result of the expression is negative,
    because even though it's a problem for my program, it isn't really a
    "ValueError," right?
     
    John Salerno, Aug 10, 2006
    #7
  8. On Wed, 09 Aug 2006 18:51:04 +0000 (GMT)
    John Salerno <> wrote:

    #> try:
    #> int(text)
    #> except ValueError:
    #> self.error_message()
    #> return False
    #> else:
    #> return True
    #>
    #> I think it's much cleaner, but obviously I lost the test in the
    #> original if statement.
    #>
    #> So my question is, can I still retain this second structure and
    #> still test for > 0, but not have any extra nesting?

    How about

    try:
    if int(text) > 0:
    return True
    except ValueError:
    pass
    self.error_message()
    return False

    --
    Best wishes,
    Slawomir Nowaczyk
    ( )

    In 10 minutes, a hurricane releases more energy than all of the world's
    nuclear weapons combined.
     
    Slawomir Nowaczyk, Aug 10, 2006
    #8
  9. John Salerno

    infidel Guest

    > > try:
    > > if int(text) <= 0: raise ValueError

    >
    > Hmm, I'm actually not so sure about this line now. It doesn't seem right
    > to raise a ValueError when the result of the expression is negative,
    > because even though it's a problem for my program, it isn't really a
    > "ValueError," right?


    It's an invalid value to your program, so yes, it is a ValueError.
     
    infidel, Aug 10, 2006
    #9
  10. John Salerno a écrit :
    > Bruno Desthuilliers wrote:
    >
    >> try:
    >> if int(text) <= 0: raise ValueError

    >
    >
    > Hmm, I'm actually not so sure about this line now. It doesn't seem right
    > to raise a ValueError when the result of the expression is negative,
    > because even though it's a problem for my program, it isn't really a
    > "ValueError," right?


    It's obviously a ValueError if your program needs a strictly positive
    integer. But anyway, you don't care: this error is raised just so it get
    caught on the next line...
     
    Bruno Desthuilliers, Aug 10, 2006
    #10
  11. John Salerno

    Boris Borcic Guest

    Slawomir Nowaczyk wrote:
    >
    > try:
    > if int(text) > 0:
    > return True
    > except ValueError:
    > pass
    > self.error_message()
    > return False
    >


    Nicely DRY. To make it even more compact, it may be noticed that the default
    return value None is false in a boolean context - so that the last line is
    superfluous if the return value is only wanted to test it in such a context.
     
    Boris Borcic, Aug 10, 2006
    #11
  12. On Thu, 10 Aug 2006 14:32:49 +0000 (GMT)
    John Salerno <> wrote:

    #> Bruno Desthuilliers wrote:
    #>
    #> > try:
    #> > if int(text) <= 0: raise ValueError
    #>
    #> Hmm, I'm actually not so sure about this line now. It doesn't seem right
    #> to raise a ValueError when the result of the expression is negative,
    #> because even though it's a problem for my program, it isn't really a
    #> "ValueError," right?

    Well, you could always do something like
    try:
    int("-"+text)
    Now, this *will* be a real ValueError for negative integers ;-) ;-) ;-)

    But no, I am not suggesting that... especially since "-0" is valid.

    --
    Best wishes,
    Slawomir Nowaczyk
    ( )

    COMMAND: A suggestion made to a computer.
     
    Slawomir Nowaczyk, Aug 10, 2006
    #12
  13. Boris Borcic a écrit :
    > Slawomir Nowaczyk wrote:
    >
    >>
    >> try:
    >> if int(text) > 0:
    >> return True
    >> except ValueError:
    >> pass
    >> self.error_message()
    >> return False
    >>

    >
    > Nicely DRY. To make it even more compact, it may be noticed that the
    > default return value None is false in a boolean context - so that the
    > last line is superfluous if the return value is only wanted to test it
    > in such a context.


    While it's technically true - and I while I have a taste for compactness
    - skipping the explicit 'return False' somehow hurts my sense of
    esthethic... Unless you propose to return object or type instead of
    True - but then it begins to look very strange !-)
     
    Bruno Desthuilliers, Aug 10, 2006
    #13
  14. John Salerno

    John Salerno Guest

    Boris Borcic wrote:
    > Slawomir Nowaczyk wrote:
    >>
    >> try:
    >> if int(text) > 0:
    >> return True
    >> except ValueError:
    >> pass
    >> self.error_message()
    >> return False
    >>

    >
    > Nicely DRY. To make it even more compact, it may be noticed that the
    > default return value None is false in a boolean context - so that the
    > last line is superfluous if the return value is only wanted to test it
    > in such a context.


    In this case the method must return False, because it's a wxPython
    method that needs a True or False value. If it doesn't, the program will
    continue even after the error message.

    What I did to make it compact was have the error_message() method return
    False, so I can just call

    return self.error_message()
     
    John Salerno, Aug 10, 2006
    #14
  15. John Salerno

    Boris Borcic Guest

    Bruno Desthuilliers wrote:
    > Boris Borcic a écrit :
    >> Slawomir Nowaczyk wrote:
    >>
    >>>
    >>> try:
    >>> if int(text) > 0:
    >>> return True
    >>> except ValueError:
    >>> pass
    >>> self.error_message()
    >>> return False
    >>>

    >>
    >> Nicely DRY. To make it even more compact, it may be noticed that the
    >> default return value None is false in a boolean context - so that the
    >> last line is superfluous if the return value is only wanted to test it
    >> in such a context.

    >
    > While it's technically true - and I while I have a taste for compactness
    > - skipping the explicit 'return False' somehow hurts my sense of
    > esthethic... Unless you propose to return object or type instead of
    > True - but then it begins to look very strange !-)


    so what about simplifying your solution 1 to

    try :
    return int(text)>0 or int('garbage')
    except ValueError :
    self.error_message()


    it still returns True on success, but hides it well :) and while perhaps a bit
    too clever I feel it competes well on readability, precisely because it is
    compact and because int('garbage') mimics int(text) while standing just before
    'except ValueError'. That mimicry kind of encapsulates your and infidel's answer
    to the OP's objection to raising ValueError if int(text)<=0.
     
    Boris Borcic, Aug 10, 2006
    #15
  16. John Salerno

    Boris Borcic Guest

    John Salerno wrote:
    > In this case the method must return False, because it's a wxPython
    > method that needs a True or False value. If it doesn't, the program will
    > continue even after the error message.


    Just as it should do if the method returns True and no error message is produced
    if I understand you well... Are you sure ? I don't know wxPython, but this
    strikes me as surprisingly unpythonic behavior.
     
    Boris Borcic, Aug 10, 2006
    #16
  17. John Salerno

    Boris Borcic Guest

    Boris Borcic wrote:
    > John Salerno wrote:
    >> In this case the method must return False, because it's a wxPython
    >> method that needs a True or False value. If it doesn't, the program
    >> will continue even after the error message.

    >
    > Just as it should do if the method returns True and no error message is
    > produced if I understand you well... Are you sure ? I don't know
    > wxPython, but this strikes me as surprisingly unpythonic behavior.


    I just verified on the wxWindows demo I had somehow installed on my box, that
    indeed returning None appears to produce the same behavior as returning True,
    distinct from the behavior obtained by returning False. Ugh...
     
    Boris Borcic, Aug 10, 2006
    #17
  18. John Salerno

    Simon Forman Guest

    John Salerno wrote:
    > Simon Forman wrote:
    >
    > > What about the version I gave you 8 days ago? ;-)
    > >
    > > http://groups.google.ca/group/comp.lang.python/msg/a80fcd8932b0733a
    > >
    > > It's clean, does the job, and doesn't have any extra nesting.
    > >
    > > Peace,
    > > ~Simon
    > >

    >
    > I remember that version, but I found it a little hard to follow. It
    > seems like the kind of code that if I look at it again in another month
    > or so, I'll have to trace through it again to figure out what's what.


    I'm sorry to hear that. I thought it was cleaner and more
    understandable than that. May I indulge in explaining it a bit? I
    can, perhaps, make it clearer.

    def Validate(self, parent):
    text = self.GetWindow().GetValue()

    # Detect whether the text is a valid int.
    try:
    # Do the integer conversion.
    T = int(text)

    except ValueError:
    # It did not convert, the test is False.
    result = False

    else:
    # It converted. T is an integer.
    # The result of the test is the Boolean
    # expression (T > 0)
    result = T > 0

    # At this point our testing is done.
    # Var result is a Boolean indicating, um,
    # the result of the test.. :)

    # Orthogonal to the test itself, report the
    # (possible) failure of the test.
    if not result: self.error_message()

    # Return the result.
    return result

    There are several good (IMHO) points to this version.

    1.) The least code possible occurs in the try..except statement.

    I feel that you should strive to put the least possible code between
    "try" and "except". A try..except block basically means, "I expect
    this error, and I know exactly what to do about it." Otherwise you
    would leave out the error check and let exceptions propagate up to
    either a higher level error handler or the user/coders themselves.

    That's kind of why exceptions exist in the first place: to indicate
    when something exceptional happened. You put a try..except statement
    in your code when an exception *wouldn't* be exceptional, i.e. when you
    know what to do about it.

    This is the flipside of "bare except: is bad".

    When you start cramming a bunch of extra statements into a try..except
    block you run the risk of catching exceptions other than the one you
    actually know what to do with. It's a source of subtle bugs, and, IMO,
    a Bad Idea.

    2.) The except clause does almost nothing.

    If a ValueError occured, the result is False. You're done. Simple, easy
    to understand, unlikely to fail or have obscure errors.

    3.) The else clause does almost nothing.

    If your control flow arrives in the else clause, you know you have a
    int T that contains the value of the integer your user entered. All
    that remains is to test T is greater than zero. Since you need that
    information twice more in your method (once to trigger error reporting
    and once more to return it) you simply assign it to a variable, reusing
    the same name that would have been assigned in the except clause.
    Because of that...

    4.) At the end of the try..except..else statement you have a result
    variable that contains the Boolean result of the test. It's pretty
    much guaranteed.

    There's no linkage between the previous part of your code and the rest
    except for that Boolean variable. Less linkage is generally a Good
    Thing. It makes your code easier to modify and debug.

    5.) The error reporting statement is completely "orthogonal" to the
    rest of the method. You could change it or comment it out or remove it
    without affecting (or having to edit) the rest of your method.

    6.) There's a single return statement.

    I forget now where I picked this up, but it's served me well for many
    years: Procedures, functions, methods, etc... should have one exit
    point. Something about having fewer "code paths" to test or something.
    Also, it makes your code easier to read and understand. Sometimes
    it's useful to violate this, but often when I think that's the case I
    find that rewriting a function to avoid it results in better code.

    7.) Last but not least, the method is made up of tiny pieces that do
    only one thing and do it well. To quote C. A. R. Hoare, "There are two
    ways of constructing a software design: One way is to make it so simple
    that there are obviously no deficiencies, and the other way is to make
    it so complicated that there are no obvious deficiencies."

    Not counting the "text = self.GetWindow().GetValue()" statement there
    are just five tiny pieces of code, each only one or two lines, and each
    doing just one step of the overall processing. Written this way, there
    are unlikely to be hidden deficiencies.

    In "if int(text) <= 0: raise ValueError", there are four things going
    on in the line: an integer conversion, a comparison, an if..then
    statement, and an "Exception raising". Not the worst code by any
    means, but more dense than it needs to be.

    Given that people can (supposedly) only handle 7+|-2 pieces of
    information at a time, having four pieces in one line is quite a few.
    Now this isn't really fair, that line is simple enough for most
    programmers to understand at a glance, but the principal still holds.
    You're not really gaining much from putting all that stuff in there
    anyway, at least in terms of length of code.

    Consider:

    import dis

    def val0(text):
    try:
    T = int(text)

    except ValueError:
    result = False

    else:
    result = T > 0

    if not result: error_message()

    return result

    def val1(text):
    try:
    if int(text) <= 0: raise ValueError

    except ValueError:
    error_message()
    return False

    else:
    return True

    dis.dis(val0)
    print '##############'
    dis.dis(val1)


    Not counting blank lines, val0 is only one line longer than val1. And
    if your run the code and examine the disassembly you'll find that val1
    only saves a couple of bytecodes.


    Well anyway, this post has gone on way longer than I wanted it to. I'd
    better get back to work.

    I hope the code is a little clearer to you. :)

    Peace,
    ~Simon

    (I think, if you count this post, that's the second most extensively
    documented function I've ever written. LOL)

    > But I think it was your code that made me think of using an else
    > statement in the first place! :)


    Yeah, I forget about else's too sometimes. :) for statements can
    have an else.. that slips my mind all the time hahaha
     
    Simon Forman, Aug 11, 2006
    #18
  19. On Thu, 10 Aug 2006 16:42:47 -0700
    Simon Forman <> wrote:

    #> 6.) There's a single return statement.
    #>
    #> I forget now where I picked this up, but it's served me well for
    #> many years: Procedures, functions, methods, etc... should have one
    #> exit point. Something about having fewer "code paths" to test or
    #> something.

    Number of return statements has absolutely *nothing* to do with number
    of code paths to test.

    --
    Best wishes,
    Slawomir Nowaczyk
    ( )

    Only drug dealers and software companies call their customers 'users.'
     
    Slawomir Nowaczyk, Aug 11, 2006
    #19
  20. John Salerno

    John Salerno Guest

    Boris Borcic wrote:
    > Boris Borcic wrote:
    >> John Salerno wrote:
    >>> In this case the method must return False, because it's a wxPython
    >>> method that needs a True or False value. If it doesn't, the program
    >>> will continue even after the error message.

    >>
    >> Just as it should do if the method returns True and no error message
    >> is produced if I understand you well... Are you sure ? I don't know
    >> wxPython, but this strikes me as surprisingly unpythonic behavior.

    >
    > I just verified on the wxWindows demo I had somehow installed on my box,
    > that indeed returning None appears to produce the same behavior as
    > returning True, distinct from the behavior obtained by returning False.
    > Ugh...


    But since None == False is false, isn't it right that returning None
    wouldn't necessarily behave like returning False?
     
    John Salerno, Aug 11, 2006
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Salerno
    Replies:
    4
    Views:
    411
    John Salerno
    Jul 28, 2006
  2. Ed Jensen

    try/except/else/finally problem

    Ed Jensen, Jun 28, 2007, in forum: Python
    Replies:
    5
    Views:
    455
    Ben Finney
    Jun 29, 2007
  3. Fabio Z Tessitore

    who is simpler? try/except/else or try/except

    Fabio Z Tessitore, Aug 12, 2007, in forum: Python
    Replies:
    5
    Views:
    385
  4. kj
    Replies:
    15
    Views:
    582
    Lawrence D'Oliveiro
    May 23, 2009
  5. David House

    try -> except -> else -> except?

    David House, Jul 6, 2009, in forum: Python
    Replies:
    2
    Views:
    356
    Bruno Desthuilliers
    Jul 6, 2009
Loading...

Share This Page