Raising an exception - python

This is a homework problem. I've been trying to solve it but couldn't get the right result.
This is the question:
Write a function string2int that attempts to convert a string to an integer. If the string does represent a positive integer then that integer should be returned. If the string does not represent a positive integer then you should raise a syntax exception (raise SyntaxError('not an integer')).
You may choose to use the (already defined) function all_digits that takes a string and returns True if all the characters of the string are digits and False otherwise.
What I've got so far is:
try all_digits is True:
return int(num)
except all_digits is False:
raise SyntaxError('not an integer')
Because I'm using an already defined function, I didn't define a function (or did I get it wrong?).
Can anyone have a look at my code please? Much appreciated.

I can guess, but you might want to tell us what kind of error you get when you execute the code (just a heads up for the next time you ask a question).
There's a couple of mistakes:
1) The syntax of
try all_digits is True:
is wrong. The "try" statement should look like this:
try:
<your code>
except <type of exception to catch>:
<error handling code>
2) You said "all_digits" is a function. Therefore, the code
all_digits is True
should be
if all_digits(num):
Putting it all together:
def string2int(num):
if all_digits(num):
return int(num)
raise SyntaxError('not an integer')

In addition to Rawing's excellent answer, note that the usual time to use try/except is when you can handle the error thrown in the try block and continue as usual. For instance:
def add_three(x) -> int:
try:
return x + 3
except TypeError:
# someone passed a non-int/float to the function!
# let's try and coerce it.
return int(x) + 3
# if this still throws an exception, we'll let it
# raise its own TypeError exception!
In your case it looks like you're just doing regular conditionals, so use if all_digits(num): return int(num) else: raise TypeError('not an integer')

all_digits(string) function:
First, it's good to understand what does the pre-defined all_digits(string) function do. Following is a sample implementation of that function, which works as desired by your description. It checks whether each letter of the string is a digit and returns a boolean, True or False, accordingly:
def all_digits(string):
''' only returns True if all characters of the string are Integers '''
for l in string:
if l.isdigit(): pass
else: return False
return True
string2num(string) function with raise statement:
Now, we can use this function in our error handling block of the string2num(string) function. Since your problem requires you to only raise a specific exception and not to continue with an alternate block of code, you do not need the try: ... except: block.
With the proper syntax of the raise statement, we can write:
def string2num( string = '-23'):
if all_digits(string):
return int('23')
raise SyntaxError("not an integer")
and we get:
>>> string2num()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 4, in string2num
SyntaxError: not an integer
with try: ... except: ... block:
But if you do want to execute an alternate block of code when the exception is raised, you can use the try: ... except: block syntax. You may need it, for instance, if you want to further check if the string is a negative integer and if so then return the negative integer:
def string2num( string = '-23'):
try:
if all_digits(string):
return int(string)
raise SyntaxError("not an integer")
except SyntaxError:
#alternate code goes here#
try:
return int(string)
except ValueError:
print "string contains an alphabet"
This will produce:
>>> string2num()
-23
>>> string2num('ab2')
string contains an alphabet
Style for if statement:
As a side note on your style, you don't have to explicitly write whether an expression evaluates to True or False in an if statement, like so:
if all_digits(string) is True:
Since all_digits(string) returns a boolean, you can equivalently just say if True, like so:
if all_digits(string):

Related

Building a function to capture non-blank 'Int' from the User in Python

I have a command line console game where I'm trying to capture integers from the User (the game is for learning times tables). I built a re-usable function to handle this which is working for blank answers, however will break if the User enters a string (e.g. "abc").
Can someone help me extend this function to filter out strings as well? (e.g. they should be asked to enter a number). I believe the error is being caused by the casting of int() in the return statement, however I need the input to be an Int for answer-checking comparison purposes (e.g. I compare their input to a stored answer, for which I need an Int == Int comparitor).
Function:
# gets a non-blank int from the User, printing a prompt and optionally displaying an error prompt
def get_int(prompt, err_prompt):
input_int = ""
# if entry is blank
while not input_int:
try:
# enter input
input_int = input(prompt)
if not input_int:
raise ValueError(err_prompt)
except ValueError as err:
print(err)
return int(input_int)
Calling the function example:
# get a non-blank int from the User as a guess
# note 'current_q' is a string from a list, e.g. "2 x 2 = " which acts as the prompt
guess = get_int(current_q, "You don't have a lot of other options. Try guessing a number...\n")
Example of the code working for blank answers:
Example of the code not working for strings:
Your if statement is wrong here:
if not input_int:
raise ValueError(err_prompt)
It will not raise an error if the value is not an empty string since its 'truthy'.
Instead you need to manually convert the input to an int and catch error:
def get_int(prompt, err_prompt):
input_int = ""
# if entry is blank
while not input_int:
try:
input_int = int(input(prompt)) # convert input to int
except ValueError:
print(err_prompt) # if the value can't be converted simply print your existing error prompt
return input_int
You could use isdigit on the input to check if it's an int before you cast it.
def get_int(prompt:str, error:str):
while not (u:=input(prompt)).isdigit():
print(error)
return int(u)

Restrict user from entering non-numeric value

I am writing a code which should use only def (). input() cannot be used inside def(). When a numeric value is passed, it is okay. When string (e.g. Five, ten, etc.) is provided, it returns NameError. How to fix this issue using try and except.
def num(n):
try:
return int(n)
except NameError:
print('Error: Enter numeric nalue')
return
num(Five)
output: NameError: name 'Five' is not defined
I am using Anaconda 2020.2 (Python3.7.6 64-bit)
Two issues:
When you are trying to test your routine with a string value, you are trying to code the string constant without the quotes, so that it is instead trying to look up the non-existent variable Five, hence the NameError. You need quotes around it (i.e. 'Five').
Inside your function, the relevant exception to look for in the case of non-numeric data is a ValueError.
Example with these corrected:
def num(n):
try:
return int(n)
except ValueError:
print('Error: Enter numeric value')
return
print(num('Five'))
below
(the idea is to return a tuple from the function call. first tuple element is a boolean that tells if we have a valid int. second tuple element can be the actual int or a string that explains why we cant convert the input to int)
def to_int(val):
try:
result = int(val)
return True,result
except ValueError:
return False,'{} is not a valid integer'.format(val)
print(to_int(7))
print(to_int('ttt'))
output
(True, 7)
(False, 'ttt is not a valid integer')
Try this :
def num(n):
try :
return int(n)
except:
raise NameError("Please provide an integer value")
num('five')

How to differentiate between cases of ValueError

Since too many python operations return ValueError, how can we differentiate between them?
Example: I expect an iterable to have a single element, and I want to get it
a, = [1, 2]: ValueError: too many values to unpack
a, = []: ValueError: too few values to unpack
How can I differentiate between those two cases?? eg
try:
a, = lst
except ValueError as e:
if e.too_many_values:
do_this()
else:
do_that()
I realise that in this particular case I could find a work-around using length/indexing, but the point is similar cases come up often, and I want to know if there's a general approach. I also realise I could check the error message for if 'too few' in message but it seems a bit crude.
try:
raise ValueError('my error')
except ValueError as e:
# use str(), not repr(), see
# https://stackoverflow.com/a/45532289/7919597
x = getattr(e, 'message', str(e))
if 'my error' in x:
print('got my error')
(see also How to get exception message in Python properly)
But this might not be a clean solution after all.
The best thing would be to narrow the scope of your try block so that only one was possible. Or don't depend on exceptions to detect those error cases.
This isn't really an answer, because it only applies if you have some control over how the exceptions are raised. Since exceptions are just objects, you can just tack on other objects / flags to them. Not saying that this is a great thing to do or a great way of doing it:
from enum import Enum
class ValueErrorType(Enum):
HelloType = 0,
FooType = 1
def some_func(string):
if "Hello" in string:
error = ValueError("\"Hello\" is not allowed in my strings!!!!")
error.error_type = ValueErrorType.HelloType
raise error
elif "Foo" in string:
error = ValueError("\"Foo\" is also not allowed!!!!!!")
error.error_type = ValueErrorType.FooType
raise error
try:
some_func("Hello World!")
except ValueError as error:
error_type_map = {
ValueErrorType.HelloType: lambda: print("It was a HelloType"),
ValueErrorType.FooType: lambda: print("It was a FooType")
}
error_type_map[error.error_type]()
I'd be curious to know if there is some way you can achieve this with exceptions where you have no control over how they're raised.

Python's way to store a default value if expression failed

What is the python analog of perl's // operator?
In perl, one can do something like :
$pos = $some_list[0] // 1
How do you accomplish the same in python?
In Python there is no undefined; instead, you'd get an exception if you tried to access an non-existent index in a list. As such, you can use exception handling instead:
try:
pos = some_list[0]
except IndexError:
pos = 1
For the first element of a sequence, you could explicitly test the sequence as a boolean (a python container is 'falsey' when empty):
post = some_list[0] if some_list else 1
How about using exceptions?
try:
pos = some_list[0]
except (NameError, IndexError):
pos = 1
An alternative to try/catch answers above for dictionaries is the default argument on .get():
param_value = my_dictionary.get(param_key, default_value)
The best practice for this in python is to handle exceptions explicitly with a try, except clause. One example presented here to help you visuallize
my_list = []
try:
item = my_list[1]
except IndexError:
item = 1
Here the code executes and an exception is raised because the index "1" is out of bounds. We then go on to handle that exception and set item=1 allowing the program to continue running. The reason for this explicit handling of exceptions is so we as programmers see exactly what is causing our problems. Take this for example:
my_list = [0]
try:
item = 1/my_list[0]
except IndexError:
item = 1
This will raise a zero division error (halting execution) and let us know that we need to handle some other exception explicitly beyond the original exception we expected, the IndexError. We might then do something like this to deal with that situation:
my_list = [0]
try:
item = 1/my_list[0]
except IndexError:
item = 1
except ZeroDivisionError:
item = 99999
try-except blocks also have a few other notable features we can exploit:
try:
# code which might raise error
pass
except IndexError as err:
# handling an index error and storing the traceback in err
pass
except ZeroDivisionError:
#handling some other error:
pass
else:
# code we would like to execute if the try block succeeds without any errors
pass
finally:
# code we will execute regardless of what occurs in the entire
# try,except,else block listed above (i.e we can ensure a file is closed)
pass

Exception Handling, function attempting to convert a string into an integer

I am trying to write a function which turns a string into an integer, with an exception to catch anything which is not a digit and return a SyntaxError. The try here should take a string and return an integer if all characters in the string are digits. An example value of (sti) would be 12a34.
def string2int(sti):
try:
int(float(sti))
if sti.isdigit():
return sti
except Exception, e:
raise SyntaxError('not an integer')
Trying to iron it out in a python visualizer gives me an attribute error on line 4:
AttributeError: 'float' object has no attribute 'isdigit'
There is an all_digits function I can use that takes a string and returns True if all the characters of the string are digits and False otherwise, but I haven't been able to get the try to work using that either.
How can I write this so that if the string does represent a positive integer then that integer is returned?
def string2int(sti):
try:
return int(sti)
except ValueError:
raise SyntaxError('not an integer')
Or:
def string2int(sti):
if sti.isdigit():
return int(sti)
else:
raise SyntaxError('not an integer')
In your function if sti is an integer, this should simply work, but just for regular integers like 123 not even "+1" or "1e2":
def string2int(sti):
try:
if sti.isdigit():
return sti
So you can use regular expressions:
import re
matchInt = re.match("^\+?[0-9]+([eE][0-9]+)?$",sti)
if matchInt:
return sti
This regular expression matches against all (positive) integers shown regularly or in scientific notation.

Categories

Resources