I tried to write a code that can distinguish the following four different errors.
TypeError: The first parameter is not an integer;
TypeError: The second parameter is not a string;
ValueError: The value of the first parameter is not in the range of 1 to 13; or
ValueError: The value of the second parameter is not one of the strings in the set {'s', 'h', 'c', 'd'}.
However, I only can get the first one to work but not the other three errors. I tried different ways to make it work, but still can't figure out what's wrong.
class Card: # One object of class Card represents a playing card
rank = ['','Ace','Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten','Jack','Queen','King']
suit = {'d':'Diamonds', 'c':'Clubs', 'h':'Hearts', 's':'Spades'}
def __init__(self, rank=2, suit=0): # Card constructor, executed every time a new Card object is created
if type(rank) != int:
raise TypeError()
if type(suit) != str:
raise TypeError()
if rank != self.rank:
raise ValueError()
if suit != 'd' or 'c' or 'h' or 's':
raise ValueError()
self.rank = rank
self.suit = suit
def getRank(self): # Obtain the rank of the card
return self.rank
def getSuit(self): # Obtain the suit of the card
return Card.suit[self.suit]
def bjValue(self): # Obtain the Blackjack value of a card
return min(self.rank, 10)
def __str__(self): # Generate the name of a card in a string
return "%s of %s" % (Card.rank[int(self.rank)], Card.suit[self.suit])
if __name__ == "__main__": # Test the class Card above and will be skipped if it is imported into separate file to test
try:
c1 = Card(19,13)
except TypeError:
print ("The first parameter is not an integer")
except TypeError:
print ("The second parameter is not a string")
except ValueError:
print ("The value of first parameter is not in the range of 1 to 13")
except ValueError:
print ("The value of second parameter is not one of the strings in the set {'s','h','c','d'}")
print(c1)
I know maybe it is due to that I have same TypeError and ValueError. Therefore, Python can't distinguish the second TypeError which I hit c1 = Card(13,13) is different from the first TypeError. So, I only get the message that "The first parameter is not an integer" when I have c1 = Card(13,13).
You are trying to distinguish between the error sources in completely the wrong place. By the time the error gets out of Card.__init__, there is no way to tell why e.g. a TypeError was thrown. For each error class (TypeError, ValueError) only the first except will ever be triggered:
try:
...
except TypeError:
# all TypeErrors end up here
except TypeError:
# this is *never* reached
except ValueError:
# all ValueErrors end up here
except ValueError:
# this is *never* reached
Instead, you should provide the specific error messages inside Card.__init__, when you actually raise the error and already know what the reason is:
if not isinstance(rank, int): # better than comparing to type
raise TypeError("The first parameter is not an integer")
Then you can handle them much more simply:
try:
c1 = Card(19,13)
except (TypeError, ValueError) as err: # assign the error to the name 'err'
print(err) # whichever we catch, show the user the message
else:
print(c1) # only print the Card if there were no errors
If you have a particular need to distinguish between different errors of the same class, you can either explicitly check the message:
except TypeError as err:
if err.args[0] == "The first parameter is not an integer":
# do whatever you need to
or create your own, more specific Exception sub-classes, so you can have separate except blocks:
class FirstParamNotIntError(Exception):
pass
(this is just an example, they are too specific in your particular case).
It's probably worth having a read through the documentation on exceptions and the tutorial on using them.
Related
I'm doing a project that is requiring a lot of input validation. I currently have a function defined as follows:
def get_valid_float(inputMessage,errorMessage):
while True:
variableInput = input(inputMessage)
try:
variableInput = float(variableInput)
return variableInput
except ValueError:
print(errorMessage)
This function allows me to choose a custom message to prompt the user. It will then validate that the user input is indeed a float, and will print a custom error message in the event that it is not. It will loop until the user gives a valid input.
However, I would rather not create a function to validate each and every data type. It seems like it would be best to combine these into one get_valid_input() function, and pass a third argument allowing me to choose what data type I am attempting to verify. For example, get_valid_input(complex,inputMessage,errorMessage).
I am obviously unable to pass a keyword as an argument. This makes me think the only way to do this would to be to do something like this:
def get_valid_float(dataType,inputMessage,errorMessage):
if dataType == "float"
while True:
variableInput = input(inputMessage)
try:
variableInput = float(variableInput)
return variableInput
except ValueError:
print(errorMessage)
elif dataType == "integer"
while True:
variableInput = input(inputMessage)
try:
variableInput = int(variableInput)
return variableInput
except ValueError:
print(errorMessage)
And so on, with an elif for every data type. Surely there is an easier way to do this, that somehow allows me to execute the line variableInput = {dataType}(variableInput) to confirm that they input a value of data type "dataType". Any ideas?
Just pass as an argument the actual data type, rather than the name of the data type. E.g:
def get_valid_input(dataType, inputMessage, errorMessage):
while True:
value = input(inputMessage)
try:
value = dataType(value)
break
except ValueError:
print(errorMessage)
You would call it like this:
floatvalue = get_valid_input(float, "enter a float value: ", "that is an invalid float")
intvalue = get_valid_input(int, "enter an integer value: ", "that is an invalid integer")
I am obviously unable to pass a keyword as an argument.
Not sure why you're saying that, but you can! :)
Also no need for error message, just catch all Exceptions (Not recommended but since you are just printing out the error it seems fine here)
The message strings aren't really needed, try using the name of the dataType and the exception's message like this:
def get_valid_data(dataType):
while True:
variableInput = input(f"Put in data of type {dataType.__name__}: ")
try:
variableInput = dataType(variableInput)
return variableInput
except Exception as e:
print(e)
get_valid_data(int)
>>> Put in data of type int: "hi"
>>> invalid literal for int() with base 10: '"hi"'
I loop though a list of currencies in order to download price series from an API and it happens that some of them are not supported so that it raises a module defined exception class : ExchangeError: This currency pair is not supported.
When it occurs I would like to continue the loop to the next currency but for some reason I'm unable to handle the module exception.
Here is an example that works fine with a built-in exception :
f = [1,2,3,4,'A',5]
def foo(nb):
return nb /2
for i in f :
try:
print(foo(i))
except TypeError :
continue
As expected it returns :
0.5
1
1.5
2
2.5
But as soon as it is a module (or user defined) exception it throws an error saying the exception is not defined :
#retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def apiFetchOHLC(obj, currency, timeframe, option):
ohlcv = obj().fetch_ohlcv(currency, timeframe, since = option)
return ohlc
for c in currencies_list :
...
try :
# Download data
ohlc = apiFetchOHLC(obj, c, tf, maxCandlesLimit)
# except : # works fine
except ExchangeError : # doesn't work
print("Oops! That was no valid currency. Continue...")
continue
This is the error I get when I run the loop :
except ExchangeError:
NameError: name 'ExchangeError' is not defined
To make it works I need to remove the exception type ExchangeError but to me it is not a workaround because it will continue the loop whatever the exception is, and sometimes I need to retry the download.
How can I achieve this with try and except or with the retrying package ? (link)
def foo(count):
try:
while(count < 10):
if(count%2 == 1):
raise Exception()
print(count)
count = count+1
except:
print( str(count) + ' -> Exception')
foo(count+1)
foo(2)
Whenever an exception occurs in a try block, handle it in except block as follows -
To continue the process you are doing in try block, you should reach the try block from an except block - keep the try block in a function, name it foo, so that you could call it from the except block
To continue from the next iteration, you need to know the previous iteration where the exception has been raised - pass an argument to that function
After identifying the problem better I have found that I needed to give the full name space of the exception class I want to catch:
for c in currencies_list :
...
try :
# Download data
ohlc = apiFetchOHLC(obj, c, tf, maxCandlesLimit)
except ccxt.ExchangeError :
print("Oops! That was no valid currency. Continue...")
continue
In Python 3.x, is it possible to run a function before an item gets appended to a list ?
I have a class which inherits from a list, with some additionnal custom functions. I would like a series of checks to be performed on the data of any element which gets added to this list. If an added element does not meet certain criteria, the list will raise an error.
class ListWithExtraFunctions(list):
def __beforeappend__(self):
... run some code ...
... perform checks ...
... raise error if checks fail ...
Define ListWithExtraFunctions.append and call super().append(value) if value passes all the checks:
class ListWithExtraFunctions(list):
def append(self, value):
if okay():
return super().append(value)
else:
raise NotOkay()
This option is very similar to the solution that Vaultah wrote. It only uses “try…except” that will allow you to handle exceptions in certain way.
class Nw_list(list):
def val_check(self, value):
# Accepts only integer
if type(value) == int:
return value
else:
# Any other input type will raise exception
raise ValueError
def append(self, value):
try:
# Try to append checked value
super().append(self.val_check(value))
except ValueError:
# If value error is raised prints msg
print("You can append only int values")
I want to create custom error messages for a function.
def tr( launch_speed , launch_angle_deg , num_samples ):
#Error displays
try:
launch_speed>0
except:
raise Exception("Launch speed has to be positive!")
try:
0<launch_angle_deg<90
except:
raise Exception("Launch angle has to be 0 to 90 degrees!")
try:
um_samples = int(input())
except:
raise Exception("Integer amount of samples!")
try:
num_samples >=2
except:
raise Exception("At least 2 samples!")
Essentially, what I want is to get an error message every time a wrong value has been written in the function variables, and I've tried creating these messages based on what I've gathered on the Internet, but it doesn't seem to work.
You can't use try: except: for everything; for example, launch_speed>0 will not raise an error for negative values. Instead, I think you want e.g.
if launch_speed < 0: # note spacing, and if not try
raise ValueError("Launch speed must be positive.") # note specific error
You should also test for and raise more specific errors (see "the evils of except"), e.g.:
try:
num_samples = int(raw_input()) # don't use input in 2.x
except ValueError: # note specific error
raise TypeError("Integer amount of samples!")
You can see the list of built-in errors in the documentation.
Why not go one step further and build your own exception types? There's a quick tutorial in the docs which could be used something like:
class Error(Exception):
"""Base class for exceptions defined in this module"""
pass
class LaunchError(Error):
"""Errors related to the launch"""
pass
class LaunchSpeedError(LaunchError):
"""Launch speed is wrong"""
pass
class LaunchAngleError(LaunchError):
"""Launch angle is wrong"""
pass
class SamplesError(Error):
"""Error relating to samples"""
pass
In this case the default functionality of Exception is fine, but you may be able to get finer granularity in what you catch by defining extra exceptions.
if launch_speed < 0:
raise LaunchSpeedError("Launch speed must be positive")
if 0 <= launch_angle < 90:
raise LaunchAngleError("Launch angle must be between 0 and 90")
um_samples = input()
try:
um_samples = int(um_samples)
except ValueError:
raise SampleError("Samples must be an integer, not {}".format(um_samples))
if um_samples < 2:
raise SampleError("Must include more than one sample, not {}".format(str(um_samples)))
Is there any way to return back and repeat the instruction that was handling an exception in Python?
E.g. if we get some data by input() method, and for some reason is caused an exception (e.g. when trying to convert the input string into int), we raised the exception, but after the exception, I would like again to go to the same line where the input() is.
Just note, "continue" is not an option, even if it is in a loop, because it could be several different input() assigning them to a different variables in different parts of the loop.
So the question again is:
while 1:
try:
foo = int(input(">")
...some other code here...
bar = int(input(">")
...some other code here...
fred = int(input(">")
...some other code here...
except Exception:
... do something for error handling and ...
jump_back_and_repeat_last_line_that_caused_the_exception
Imagine that the above code could be in a loop, and the exception can be caused in any instruction (foo... bar... fred...etc, or even can be any other line). So, if it fails in the "bar" line, it should try again the "bar" line.
Is there any reserved word to do this in python?
Define a function; Handle exception there.
def read_int():
while 1:
try:
value = int(input('>'))
except ValueError:
# Error handling + Jump back to input line.
continue
else:
return value
while 1:
foo = read_int()
bar = read_int()
fred = read_int()
There might be a way to do that, but it will probably result with a very poor design.
If I understand you correctly, then your problem is with the exception caused by calling input.
If that is indeed the case, then you should simply implement it in a separate method, which will handle the exception properly:
foo = getUserInput()
...some other code here...
bar = getUserInput()
...some other code here...
fred = getUserInput()
...some other code here...
def getUserInput():
while 1:
try:
return int(input(">"))
except Exception:
pass
don't do nothing in except:
while 1:
try:
a=int(raw_input('input an integer: ')) #on python2 it's "raw_input" instead of "input"
break
except ValueError, err:
# print err
pass
print 'user input is:', a
output is:
D:\U\ZJ\Desktop> py a.py
input an integer: a
input an integer: b
input an integer: c
input an integer: 123
user input is: 123