41
a = raw_input('How much is 1 share in that company? ')

while not a.isdigit():
    print("You need to write a number!\n")
    a = raw_input('How much is 1 share in that company? ')

This only works if the user enters an integer, but I want it to work even if they enter a float, but not when they enter a string.

So the user should be able to enter both 9 and 9.2, but not abc.

How should I do it?

martineau
  • 119,623
  • 25
  • 170
  • 301
Peter Nolan
  • 421
  • 1
  • 4
  • 4

8 Answers8

43

EAFP

try:
    x = float(a)
except ValueError:
    print("You must enter a number")
dan04
  • 87,747
  • 23
  • 163
  • 198
  • 12
    EAFP = Easier to Ask for Forgiveness than Permission (see http://docs.python.org/glossary.html) – Steven Rumbalski Nov 09 '10 at 20:29
  • @Steven Rumbaski Although I prefer the form: "It's easier to beg for forgiveness than ask permission" :-) –  Nov 09 '10 at 20:37
  • Alright, so is there a simple way to also check to make sure the user does not enter a negative value? – Peter Nolan Nov 09 '10 at 20:52
  • Also, I tried using that but it only works once, if you try and enter a letter or a str more than once you still get an error.. – Peter Nolan Nov 09 '10 at 20:56
38

The existing answers are correct in that the more Pythonic way is usually to try...except (i.e. EAFP, "easier to ask for forgiveness than permission").

However, if you really want to do the validation, you could remove exactly 1 decimal point before using isdigit().

>>> "124".replace(".", "", 1).isdigit()
True
>>> "12.4".replace(".", "", 1).isdigit()
True
>>> "12..4".replace(".", "", 1).isdigit()
False
>>> "192.168.1.1".replace(".", "", 1).isdigit()
False

Notice that this does not treat floats any different from ints however. You could add that check if you really need it though.

Cam Jackson
  • 11,860
  • 8
  • 45
  • 78
  • 1
    I like this solution more than the other 3 up there. – J Agustin Barrachina Mar 13 '20 at 13:46
  • `''.replace(".", "", 1).isdigit()` -> `True` :-) – jcsahnwaldt Reinstate Monica Oct 08 '21 at 13:44
  • @jcsahnwaldtReinstateMonica I don't know what this ` ''` is, but `''.isdigit()` is already `True` without replacing the dot. Can you elaborate a bit on this string? – colidyre Jul 15 '23 at 11:29
  • @colidyre The solution in this answer accepts a string like `''`, but that's probably not what the OP wanted. For example, `float('')` throws an error. – jcsahnwaldt Reinstate Monica Jul 16 '23 at 12:15
  • 1
    The string `''` contains the Kharosthi digits two, three and four. https://www.unicode.org/charts/PDF/U10A00.pdf Python's `isdigit()` accepts "digits which cannot be used to form numbers in base 10, like the Kharosthi numbers." https://docs.python.org/3/library/stdtypes.html#str.isdigit Fundamentally, `float()` and `isdigit()` have very different purposes. It's usually not a good idea to use one of these when what you really need is the other. – jcsahnwaldt Reinstate Monica Jul 16 '23 at 12:27
16

Use regular expressions.

import re

p = re.compile('\d+(\.\d+)?')

a = raw_input('How much is 1 share in that company? ')

while p.match(a) == None:
    print "You need to write a number!\n"
    a = raw_input('How much is 1 share in that company? ')
Peter C
  • 6,219
  • 1
  • 25
  • 37
  • 3
    Those regular expressions are so damn flexible! Still, dan04's solution feels much more pythonic. Here I define pythonic as "between two solutions of equivalent complexity, prefer the one that doesn't use regular expressions." That still leaves many applications for regular expressions. – Steven Rumbalski Nov 09 '10 at 20:43
  • @Steven Rumbalski: Yeah, dan04's solution seems more Pythonic, thought mine may actually be less code. – Peter C Nov 10 '10 at 01:16
  • 1
    Unfortunately this isn't a good regex for floats and so doesn't really answer the question. For example it doesn't match ".1" but DOES match "5abc". There are also negative values and other notations, so it is probably ideal to use dan04's answer, turning it into a function if you need to use it in a list comprehension or argument. – mrooney Sep 18 '13 at 20:03
  • @mrooney: I wrote this like 3 years ago. I definitely think dan04's answer is much better now. However, simply changing the regex to /^\d+(\.\d+)?$/ should be fine. I don't think matching .1 is much of a necessity, and he doesn't want to match negative numbers. – Peter C Sep 18 '13 at 21:21
12

Building on dan04's answer:

def isDigit(x):
    try:
        float(x)
        return True
    except ValueError:
        return False

usage:

isDigit(3)     # True
isDigit(3.1)   # True
isDigit("3")   # True
isDigit("3.1") # True
isDigit("hi")  # False
Phlox Midas
  • 4,093
  • 4
  • 35
  • 56
  • the problem here, if you supply a boolean or None. In the case of boolean, it will be True, in case of None an exception. –  May 14 '21 at 15:39
  • and any other data types that fail on "float(x)" without "ValueError" –  May 14 '21 at 15:50
5
s = '12.32'
if s.replace('.', '').replace('-', '').isdigit():
    print(float(s))

Note that this will work for negative floats as well.

Michael
  • 876
  • 9
  • 29
Jigyasu Tailor
  • 153
  • 2
  • 8
3

I think @dan04 has the right approach (EAFP), but unfortunately the real world is often a special case and some additional code is really required to manage things—so below is a more elaborate, but also a bit more pragmatic (and realistic):

import sys

while True:
    try:
        a = raw_input('How much is 1 share in that company? ')
        x = float(a)
        # validity check(s)
        if x < 0: raise ValueError('share price must be positive')
    except ValueError, e:
        print("ValueError: '{}'".format(e))
        print("Please try entering it again...")
    except KeyboardInterrupt:
        sys.exit("\n<terminated by user>")
    except:
        exc_value = sys.exc_info()[1]
        exc_class = exc_value.__class__.__name__
        print("{} exception: '{}'".format(exc_class, exc_value))
        sys.exit("<fatal error encountered>")
    else:
        break  # no exceptions occurred, terminate loop

print("Share price entered: {}".format(x))

Sample usage:

> python numeric_input.py
How much is 1 share in that company? abc
ValueError: 'could not convert string to float: abc'
Please try entering it again...
How much is 1 share in that company? -1
ValueError: 'share price must be positive'
Please try entering it again...
How much is 1 share in that company? 9
Share price entered: 9.0

> python numeric_input.py
How much is 1 share in that company? 9.2
Share price entered: 9.2
martineau
  • 119,623
  • 25
  • 170
  • 301
1
import re

string1 = "0.5"
string2 = "0.5a"
string3 = "a0.5"
string4 = "a0.5a"

p = re.compile(r'\d+(\.\d+)?$')

if p.match(string1):
    print(string1 + " float or int")
else:
    print(string1 + " not float or int")

if p.match(string2):
    print(string2 + " float or int")
else:
    print(string2 + " not float or int")

if p.match(string3):
    print(string3 + " float or int")
else:
    print(string3 + " not float or int")

if p.match(string4):
    print(string4 + " float or int")
else:
    print(string4 + " not float or int")

output:
0.5 float or int
0.5a not float or int
a0.5 not float or int
a0.5a not float or int
Yakir GIladi Edry
  • 2,511
  • 2
  • 17
  • 16
1

The provided answers fail if the string contains some special characters such as underscore (e.g. '1_1'). The following function returns correct answer in all case that I tested.

def IfStringRepresentsFloat(s):
try:
    float(s)
    return str(float(s)) == s
except ValueError:
    return False
Hamid Heydarian
  • 802
  • 9
  • 16