I have a function to validate some user input (val
) that begins as string input. I want the input to end up in integer format but I don't want to strip a number like 4.2 to make it 4. Instead, I want to throw an error and explain the problem to the user with a different message for each invalid type.
- The function should first check that the input is not an empty string.
- Then it looks for a minus sign or hypen and throws an error if any are found.
- Next, it should check whether the string is suitable to be cast as an integer (e.g. "42"). I've used
isdigit()
for this.- If suitable, it should skip straight to converting
val
to anint
withval = int(val)
.
- If suitable, it should skip straight to converting
However, if the value is not a whole number in string form, I really want to check whether it's:
- a decimal number that can be converted without causing confusion (4.0)
- a decimal number that should prompt an error message (4.2)
- or a non-numerical string that should prompt an error message ("I am a string").
You can see this attempt in the code:
try:
if val == "":
raise ValueError("customer error 1")
if "-" in val:
raise ValueError("custom error 2")
if not val.isdigit():
try:
val = float(val)
except Exception:
raise ValueError("custom error 3")
else:
if val % 1 != 0:
raise ValueError("custom error 4")
else:
val = int(val)
else:
val = int(val)
if val < 5:
raise ValueError("custom error 5")
except ValueError as e:
print(e)
return False
except TypeError as e:
print(e)
return False
return True
After the few string operations, I'm using a nested try/except to attempt converting val
to a float. If successful, the program should find out whether that float has a remainder when divided by 1. In other words, can it be made a whole number without changing its numerical value (4.0 -> 4). If yes, go ahead an make it an integer.
If the float has a remainder when divided by 1, it must be something like 4.2, rather than 4. In that case, I want to alert the user with a particular error message.
If it can't be made a float, it must be non-numerical string like "I am a string". In that case, I want to alert the user with a different message.
If it's successfully converted to an integer at any stage, I want to ensure it's not less than 5.
The code above conveys all the right error messages, until I enter a float like 7.0
. I thought I'd handled this and it would be converted, but I'm evidently going wrong somewhere. I've re-written it so many times at this stage, I have to concede defeat and hope someone can explain where I going wrong.
I should say, I'm new to Python and to error handling and try/except/else/finally in particular, so simplified responses would be helpful!
Solved: An update for anyone else in the same position. The problem is in the handling of
val
immediately after the function runs. This is a validation function and is not the function that originally requests the input. Therefore, any conversion inside this function does not affectval
once this function is finished running sinceval
is not returned.Solution in this case was to run the above validation function and then, in the caller function, convert
val
first to float (val = float(val)
) and then to int (I usedval = int(val)
.Although the desired type is
int
,val
is still a string inside the caller function, so it is necessary first to convert it to a string and then to an integer. Trying to go directly from integer to string fails if using theint()
method. I'm unsure whether that's a widespread issue or not, but I gather it's often preferable to cast to float first and then to int.The bigger lesson, for me anyway, is to provide the context of any function shared for troubleshooting. If I had provided the preceding def line, it would have been clear that the input is gathered in a separate function, and someone would probably have suggested the cause.