I'm creating a function right now that takes in two lists. I want these two lists to be of equal size. I'm trying to figure out what kind of exception I should throw (or If I should throw an exception at all) if they aren't the same size. I kind of want to say ValueError but this is a check that doesn't actually pertain to any single value.
For clarities sake, here's my function stub.
def create_form(field_types, field_discriptions):
pass
I would just use assert and raise an AssertionError:
assert len(field_types) == len(field_descriptions), "Helpful message"
Otherwise, ValueError with a message seems like the best choice.
You can create your own subclass of exception called ArraysNotEqualSizeException. Might be a bit overkill, but it gets the point across.
throw an exception as the first thing in the function. A function-critical error should not do anything without making sure it can do what it wants or it could have bad effects
This isn't a giant error; you should use an assert
Places to consider putting assertions:
checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable
-Python wiki
assert len(listone) == len(listtwo),
"the function cannot continue because\
the two arguments passed are of invalid length"
a ValueError as suggested by Blender would be the right type if you want to use a generic exception, however that's usually reserved for larger issues and would be less helpful.
for quick reference:
"ValueError
Raised when a built-in operation or function receives an argument that
has the right type but an inappropriate value, and the situation is
not described by a more precise exception such as IndexError." -Python docs
Related
Background
I am new to python and I am writing a simple function but I am also interested in learning to do things the correct / pythonic way as I progress in my journey.
Lets consider the function below
def test_func(nested_lists,val):
return
I am expecting two arguments. One argument would be a list containing more lists. Something like this [[1,2,3,],[4,5,6,]...]. The second argument could be a value like 1.
If someone say for instance passes in a single value as the first argument and an array as the second argument. My code as it is currently returning the correct output which is 0 , However is there another way that i should be handle this?
For example should I be doing something like this
if(type(value) == list):
return 0
Or do i not need to do anything because my function is returning 0 anyway.
I know this maybe a very basic question so please forgive me but coming from a java background I am new to python so i am not sure how to handle such scenarios in python.
The other answer illustrates the proper way to check in advance for problems you can foresee. I'll provide a different approach.
The idiomatic solution in python is to "ask forgiveness, not permission". There are a lot of things that can go wrong, and where other languages might ask you to foresee all those problems and address them manually, python encourages just handling them as they happen. I would recommend doing:
def test_func(nested_lists, val):
try:
...
except TypeError:
# do whatever error-handling behavior you need to
# either throw a custom exception or return a specific value or whatever
return 0
and then designing your code in such a way that, if nested_lists and values are not compatible types, then they throw a TypeError (e.g. trying to iterate through nested_lists should fail if nested_lists is not a list. You can experiment with this behavior in a python console, but in general trying to do something to a variable that doesn't work because it's not the right type will produce a TypeError).
If your current code is working correctly, there is no pressing need to change anything. However, there are some reasons you might want to code more defensively;
If the code will seem to work correctly when you pass in bogus values, it would be better if it raised an exception instead of return a bogus value. The responsibility to call it correctly lies squarely with the caller, but enforcing it can help make sure the code is correct.
if not isinstance(nested_lists,list):
raise ValueError('Need a list, got {0!r}'.format(nested_lists))
This has the drawback that it hardcodes list as the type for the first argument; properly reusable code should work with any type, as long as it has the required methods and behaviors to remain compatible with your implementation. Perhaps instead check for a behavior:
try:
something involving nested_lists[0][0]
except (IndexError, AttributeError):
raise ValueError('Expected nested list but got {0!r}'.format(nested_lists))
(The try is not strictly necessary here; but see below.)
If you get a traceback when you call the code incorrectly, but it is opaque or misleading, it is more helpful to catch and explicitly point out the error earlier. #or example, the snippet above (without the try wrapper) would produce
Traceback (most recent call last):
module __main__ line 141
traceback.print_exc()
module <module> line 1
test_func(1,1)
module <module> line 2
AttributeError: 'int' object has no attribute '__getitem__'
which is somewhat unobvious to debug.
If the code will be used by third parties, both of the above considerations will be more important from a support point of view, too.
Notice how the code raises an exception when called incorrectly. This is generally better than silently returning some garbage value, and the caller can similarly trap the error with a try/except if this is well-defined (i.e. documented!) behavior.
Finally, since Python 3.5, you have the option to use type annotations:
def test_func(nested_lists: list, val: int) -> int:
...
As noted in the documentation, the core language does not (yet?) enforce these type checks, but they can help static code analysis tools point out possible errors.
Right now I have a function that excepts a string and is supposed to return none if the user either:
doesn't pass in a string OR
a specific exception is thrown when performing an operation on the argument
Here is how I am doing this:
def fnctn(num_str):
if not isinstance(num_str, str):
return None
try:
num = phonenumbers.parse(num_str, 'US')
except phonenumbers.phonenumberutil.NumberParseException:
return None
...
I am just wondering if there is a cleaner way of doing this. Like if I could check if the argument that was passed in was a string and check to see if the operation throws an exception or not on the same line?
This is probably the most reasonable way to do this in Python, I think. The downside of trying operations and then catching exceptions on objects which don't have well-defined behaviors is that they could be modified by the function you call. Checking whether it's a known type, i.e. str, as you do here, and if it is, then trying the operation keeps the kinds of weird behaviors to a minimum while still returning a valid result or an error (None in this case).
I wrote this function to use from an other function, checking if the output has the correct suffix. Before I use it I have two questions:
Is TypeError the best exception to use in this case?
Is there some built-in function I could use instead of mine?
Code:
def suffix_NameCheck(inFile):
if os.path.splitext(inFile)[1] is None:
raise TypeError('"%s" has not a suffix' % inFile)
else:
return inFile
There is not a built-in function that does this, because
Not all file systems really use extensions.
It's probably more common to just use the if statement on its own, rather than throwing it in a function.
The reason is that you're probably going to have to catch that exception somewhere (unless you intentionally want it to end the program, in which case, there's probably a better way to do it than throwing an exception, e.g. sys.exit). The code to catch the exception would be at least as involved as the code to just do the if check itself and handle it.
I would not raise an exception from a function named suffix_NameCheck, but rather return True or False instead. The normal operation of suffix_NameCheck is not affected by wether or not there is a correct suffix on the value, it's job is to check for that suffix.
The function that uses suffix_NameCheck could still raise an exception; ValueError may be a better exception to use in that case.
I think this is more appropriately a ValueError. The problem you are attempting to report is that the user provided a file path/name argument as a string that has no file extension. That's a problem with the value of the string, not anything's type.
Other answerers have a point that this sort of functionality at first glance should really be returning a bool, but given that that use case is covered simply with bool(os.path.splitext(candidate)[1]), I think that if you really do want to raise an exception here, you are already working with a lower level function that returns a bool.
Ie, what I want would be:
try:
print inExceptClause()
1/0
except Exception:
print inExceptClause()
print inExceptClause()
... which would print out:
False
True
False
I think you're going about this the wrong way. Your "use case" seems like that you can call a function from multiple points in your code, with it sometimes being called from within an exception handler. Within that function, you want to know whether or not an exception has been thrown, right?
The point is, you don't want to have that kind of logic in a function that has (or should have) no knowledge about the calling code... as ideally, most of your functions will not have.
That said, you might want to execute that function, but only partly. So I'd suggest one of two options:
Split up the function into multiple functions: one function has extra functionality, and will in turn call the other function, that has reusable functionality. Just call the function you need, when you need it.
Add a parameter to the function: a simple boolean value might be enough to in- or exclude a small part of that function.
Now, this isn't really an answer to your question, but I have the feeling you are looking at your problem at the wrong angle... hence the above suggestion.
sys.exc_info()
This function returns a tuple of three values that give information
about the exception that is currently being handled. (...) If no
exception is being handled anywhere on the stack, a tuple containing
three None values is returned.
See also these questions:
Python 3 traceback fails when no exception is active
Raising exceptions when an exception is already present in Python 3
This question already has answers here:
Is it better to use an exception or a return code in Python?
(6 answers)
Python: Throw Exception or return None? [closed]
(3 answers)
Closed 1 year ago.
What's better practice in a user-defined function in Python: raise an exception or return None? For example, I have a function that finds the most recent file in a folder.
def latestpdf(folder):
# list the files and sort them
try:
latest = files[-1]
except IndexError:
# Folder is empty.
return None # One possibility
raise FileNotFoundError() # Alternative
else:
return somefunc(latest) # In my case, somefunc parses the filename
Another option is leave the exception and handle it in the caller code, but I figure it's more clear to deal with a FileNotFoundError than an IndexError. Or is it bad form to re-raise an exception with a different name?
It's really a matter of semantics. What does foo = latestpdf(d) mean?
Is it perfectly reasonable that there's no latest file? Then sure, just return None.
Are you expecting to always find a latest file? Raise an exception. And yes, re-raising a more appropriate exception is fine.
If this is just a general function that's supposed to apply to any directory, I'd do the former and return None. If the directory is, e.g., meant to be a specific data directory that contains an application's known set of files, I'd raise an exception.
I would make a couple suggestions before answering your question as it may answer the question for you.
Always name your functions descriptive. latestpdf means very little to anyone but looking over your function latestpdf() gets the latest pdf. I would suggest that you name it getLatestPdfFromFolder(folder).
As soon as I did this it became clear what it should return.. If there isn't a pdf raise an exception. But wait there more..
Keep the functions clearly defined. Since it's not apparent what somefuc is supposed to do and it's not (apparently) obvious how it relates to getting the latest pdf I would suggest you move it out. This makes the code much more readable.
for folder in folders:
try:
latest = getLatestPdfFromFolder(folder)
results = somefuc(latest)
except IOError: pass
Hope this helps!
I usually prefer to handle exceptions internally (i.e. try/except inside the called function, possibly returning a None) because python is dynamically typed. In general, I consider it a judgment call one way or the other, but in a dynamically typed language, there are small factors that tip the scales in favor of not passing the exception to the caller:
Anyone calling your function is not notified of the exceptions that can be thrown. It becomes a bit of an art form to know what kind of exception you are hunting for (and generic except blocks ought to be avoided).
if val is None is a little easier than except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace. Seriously, I hate having to remember to type from django.core.exceptions import ObjectDoesNotExist at the top of all my django files just to handle a really common use case. In a statically typed world, let the editor do it for you.
Honestly, though, it's always a judgment call, and the situation you're describing, where the called function receives an error it can't help, is an excellent reason to re-raise an exception that is meaningful. You have the exact right idea, but unless you're exception is going to provide more meaningful information in a stack trace than
AttributeError: 'NoneType' object has no attribute 'foo'
which, nine times out of ten, is what the caller will see if you return an unhandled None, don't bother.
(All this kind of makes me wish that python exceptions had the cause attributes by default, as in java, which lets you pass exceptions into new exceptions so that you can rethrow all you want and never lose the original source of the problem.)
with python 3.5's typing:
example function when returning None will be:
def latestpdf(folder: str) -> Union[str, None]
and when raising an exception will be:
def latestpdf(folder: str) -> str
option 2 seem more readable and pythonic
(+option to add comment to exception as stated earlier.)
In general, I'd say an exception should be thrown if something catastrophic has occured that cannot be recovered from (i.e. your function deals with some internet resource that cannot be connected to), and you should return None if your function should really return something but nothing would be appropriate to return (i.e. "None" if your function tries to match a substring in a string for example).