99

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
Chris Calo
  • 7,518
  • 7
  • 48
  • 64

10 Answers10

61

This is a pretty regular scenario so I've written an "ignore_exception" decorator that works for all kinds of functions which throw exceptions 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
Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67
sharjeel
  • 5,825
  • 7
  • 34
  • 49
  • 13
    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 Feb 14 '10 at 20:21
  • Yeah, I agree with John but I voted you up for bring the cleaver. – Paul Hildebrandt Feb 14 '10 at 20:30
  • 10
    This answer is about as Pythonic as the COBOL ALTER verb. – John Machin Feb 14 '10 at 21:08
47
def intTryParse(value):
    try:
        return int(value), True
    except ValueError:
        return value, False
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
Lukas Šalkauskas
  • 14,191
  • 20
  • 61
  • 77
45

Actually there is a "built-in", single line solution that doesn't require introducing a helper function:

>>> s = "123"
>>> i = int(s) if s.isdecimal() else None
>>> print(i)
123

>>> s = "abc"
>>> i = int(s) if s.isdecimal() else None
>>> print(i)
None

>>> s = ""
>>> i = int(s) if s.isdecimal() else None
>>> print(i)
None

>>> s = "1a"
>>> i = int(s) if s.isdecimal() else None
>>> print(i)
None

See also https://docs.python.org/3/library/stdtypes.html#str.isdecimal

Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127
25

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
  • 69,862
  • 18
  • 95
  • 117
  • @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 Feb 14 '10 at 20:13
16

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
  • 91,829
  • 44
  • 175
  • 230
  • 2
    Didn't know about str.isdigit. That could be useful. Thanks. – Chris Calo Feb 14 '10 at 19:22
  • Please, take into account abyx answer. He's right and you should use your code, instead of mine. – Macarse Feb 14 '10 at 19:26
  • 4
    Not only is the EAFP style more appropriate and more Pythonic in this situation, it handles negative integers while isdigit doesn't. – John Y Feb 14 '10 at 20:04
  • 1
    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. – u0b34a0f6ae Feb 15 '10 at 03:05
  • 1
    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 :-) – u0b34a0f6ae Feb 15 '10 at 12:04
7

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

u0b34a0f6ae
  • 48,117
  • 14
  • 92
  • 101
  • 1
    Thanks. It's unfortunate that there's no built-in means of parsing an integer that doesn't raise an exception. – Chris Calo Feb 14 '10 at 19:24
  • 7
    @Christopher: Why is it unfortunate? How is try-except not built-in? –  Feb 14 '10 at 20:36
  • @Christopher: This is how it is Python's "culture" and I am sure you will find the same pattern in other places – u0b34a0f6ae Feb 14 '10 at 21:00
  • 6
    @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. – Chris Calo Feb 14 '10 at 23:39
  • 1
    "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 Feb 15 '10 at 02:57
2

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
2
myList = ['12', '13', '5', 'hope', 'despair', '69','0', '1.2']

myInts = [int(x) for x in myList if x.isdigit()]
Mack
  • 31
  • 3
1

This could be another alternative for parsing string to int

while True:
try:
    n = input("Please enter an integer: ")
    n = int(n)
    break
except ValueError:
    print("No valid integer! Please try again ...")
print("Great, you successfully entered an integer!")
Deepak Verma
  • 617
  • 5
  • 18
-3
def parseint(string):
    result = '0'
    for x in string:
        if x.isdigit():
        result+=x
    else:
        return int(result)
    return int(result)
  • 2
    Bad idea. It produces bogus results. Assuming the indentation on line 5 is fixed, parseint('-3') => 3; parseint('2by2') => 22; etc. Note also that the first return is always executed since there's no break in the loop. – Vroo Feb 06 '14 at 16:54