views:

206

answers:

5

I had to write the following function to fail gracefully when trying to parse a string to an integer. I would imagine Python has something built in to do this, but I can't find it. If not, is there a more Pythonic way of doing this that doesn't require a separate function?

def try_parse_int(s, base=10, val=None):
  try:
    return int(s, base)
  except ValueError:
    return val

The solution I ended up using was a modification of @sharjeel's answer. The following is functionally identical, but, I think, more readable.

def ignore_exception(exception=Exception, default_val=None):
  """Returns a decorator that ignores an exception raised by the function it
  decorates.

  Using it as a decorator:

    @ignore_exception(ValueError)
    def my_function():
      pass

  Using it as a function wrapper:

    int_try_parse = ignore_exception(ValueError)(int)
  """
  def decorator(function):
    def wrapper(*args, **kwargs):
      try:
        return function(*args, **kwargs)
      except exception:
        return default_val
    return wrapper
  return decorator
+1  A: 

int() is the built-in and pythonic way, just like you have there.

It's usually easier and more common to use it directly though:

def show_square(user_input):
  """Example of using int()."""
  try:
    num = int(user_input, 10)
  except ValueError:
    print "Error" # handle not-an-integer case
    # or you may just want to raise an exception here
    # or re-raise the ValueError
  else:
    print "Times two is", num * 2

def another_example(user_input):
  try:
    num = int(user_input, 10)
  except ValueError:
    num = default
  print "Times two is", num * 2
Roger Pate
+6  A: 

No, it is already perfect. The val parameter could be better named default, though.

Documented in the official docs simply as int(x) -- x converted to integer

kaizer.se
Thanks. It's unfortunate that there's no built-in means of parsing an integer that doesn't raise an exception.
Christopher James Calo
@Christopher: Why is it unfortunate? How is try-except not built-in?
Roger Pate
@Christopher: This is how it is Python's "culture" and I am sure you will find the same pattern in other places
kaizer.se
@Roger, it's unfortunate because using try-except adds 3 lines of code everywhere I want silent failure. dict's get method embodies a similar spirit in that it does not raise an exception. And rather than write my own method to handle this situation, this seems like something that should be built in to the Python language. For example, the hypothetical method call int.try_parse('not integer', None) would just return None instead of raising ValueError. In asking this question, I was hoping someone would tell me that something similar does, in fact, exist.
Christopher James Calo
"And rather than write my own method to handle this situation, this seems like something that should be built in to the Python language" Sorry, I don't agree with this. I've Been coding in Python for 6 years. Never needed this construct. You can write a function because -- clearly -- you need this.
S.Lott
+2  A: 

I would go for:

def parse_int(s, base=10, val=None):
 if s.isdigit():
  return int(s, base)
 else:
  return val

But it's more or less the same thing.

Macarse
Didn't know about str.isdigit. That could be useful. Thanks.
Christopher James Calo
Please, take into account abyx answer. He's right and you should use your code, instead of mine.
Macarse
Not only is the EAFP style more appropriate and more Pythonic in this situation, it handles negative integers while isdigit doesn't.
John Y
@John Y: You are right. Thanks for pointing that out!
Macarse
EAFP is always better if it is possible. I wanted to suggest this, but I realized the case with unicode numeric characters is also missing, so you have to use unicode.isnumeric and it's no longer symmteric for str/unicode. And so on.
kaizer.se
And the hexadecimal case is also missing, now you need .ishexdigit(). No, the best way to find out if you can do XX? Try doing XX! Which is exactly what the original code says :-)
kaizer.se
+6  A: 

That's the pythonic way. In python, it's customary to use EAFP style - Easier to Ask Forgiveness than Permission.
That means you'd try first, and then clean up the mess if necessary.

abyx
+1 for Easier to Ask Forgiveness than Permission.
Srinivas Reddy Thatiparthy
@abyx: I like the first two sentences. I don't like the third. When you say "then once you're sure perform the action" it sounds like you are describing LBYL, even though the first part of that sentence is more-or-less correct for EAFP. I would say "EAFP = just try, and clean up the mess if it fails".
John Y
@John - I like it! Edited my answer :) thanks
abyx
+3  A: 

This is a pretty regular scenario so I've written an "ignore_exception" decorator that works for all kinds of functions which through excpetions instead of failing gracefully:

def ignore_exception(IgnoreException=Exception,DefaultVal=None):
    """ Decorator for ignoring exception from a function
    e.g.   @ignore_exception(DivideByZero)
    e.g.2. ignore_exception(DivideByZero)(Divide)(2/0)
    """
    def dec(function):
        def _dec(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except IgnoreException:
                return DefaultVal
        return _dec
    return dec

Usage in your case:

sint = ignore_exception(ValueError)(int)
print sint("Hello World") # prints none
print sint("1340") # prints 1340
sharjeel
That's clever. Great answer.
Christopher James Calo
While this is clever, and possibly even useful if you are going to decorate a lot of functions with it, it does not answer the question as expressed in the subject line. (The answer to the subject line is: there is no built-in, and the OP's original code is already the most Pythonic way.)
John Y
Yeah, I agree with John but I voted you up for bring the cleaver.
Paul Hildebrandt
This answer is about as Pythonic as the COBOL ALTER verb.
John Machin