What's the difference between raise, try, and assert?
Asked Answered
P

9

100

I have been learning Python for a while and the raise function and assert are (what I realised is that both of them crash the app, unlike try - except) really similar and I can't see a situation where you would use raise or assert over try.

So, what is the difference between raise, try, and assert?

Phraseologist answered 21/10, 2016 at 18:7 Comment(3)
Thanks tcoc! I did indeed have a question though, it being if anyone could show me a situation when you can only use one of these functions.Phraseologist
@Phraseologist There are such examples in the python docs. Also you should ask your question in the question, not in a comment.Koster
Does this answer your question? What are the implications of using assert vs. raise ExceptionCountless
B
73

Assert:

Used when you want to "stop" the script based on a certain condition and return something to help debug faster:

list_ = ["a","b","x"]
assert "x" in list_, "x is not in the list"
print("passed") 
#>> prints passed

list_ = ["a","b","c"]
assert "x" in list_, "x is not in the list"
print("passed")
#>> 
Traceback (most recent call last):
  File "python", line 2, in <module>
AssertionError: x is not in the list

Raise:

Two reasons where this is useful:

1/ To be used with try and except blocks. Raise an error of your choosing, could be custom like below and doesn't stop the script if you pass or continue the script; or can be predefined errors raise ValueError()

class Custom_error(BaseException):
    pass

try:
    print("hello")
    raise Custom_error
    print("world")
except Custom_error:
    print("found it not stopping now")

print("im outside")

>> hello
>> found it not stopping now
>> im outside

Noticed it didn't stop? We can stop it using just exit(1) in the except block.

2/ Raise can also be used to re-raise the current error to pass it up the stack to see if something else can handle it.

except SomeError, e:
     if not can_handle(e):
          raise
     someone_take_care_of_it(e)

Try/Except blocks:

Does exactly what you think, tries something if an error comes up you catch it and deal with it however you like. No example since there's one above.

Bicarbonate answered 21/10, 2016 at 18:24 Comment(1)
Note, that like Ian Goldby mentioned, assertions are removed on optimization. assert statements are meant as internal consistency checks of the implementation. For errors which might occur during the intended use of your code, raise errors instead.Hellas
D
130

The statement assert can be used for checking conditions at runtime, but is removed if optimizations are requested from Python. The extended form is:

assert condition, message

and is equivalent to:

if __debug__:
    if not condition:
        raise AssertionError(message)

where __debug__ is True if Python was not started with the option -O.

So the statement assert condition, message is similar to:

if not condition:
    raise AssertionError(message)

in that both raise an AssertionError. The difference is that assert condition, message can be removed from the executed bytecode by optimizations (when those are enabled--by default they are not applied in CPython). In contrast, raise AssertionError(message) will in all cases be executed.

Thus, if the code should under all circumstances check and raise an AssertionError if the check fails, then writing if not condition: raise AssertionError is necessary.

Draughtboard answered 21/10, 2016 at 18:13 Comment(1)
Just note that assert statements are removed when the compilation is optimized, whereas raise AssertionError() is not.Gracie
B
73

Assert:

Used when you want to "stop" the script based on a certain condition and return something to help debug faster:

list_ = ["a","b","x"]
assert "x" in list_, "x is not in the list"
print("passed") 
#>> prints passed

list_ = ["a","b","c"]
assert "x" in list_, "x is not in the list"
print("passed")
#>> 
Traceback (most recent call last):
  File "python", line 2, in <module>
AssertionError: x is not in the list

Raise:

Two reasons where this is useful:

1/ To be used with try and except blocks. Raise an error of your choosing, could be custom like below and doesn't stop the script if you pass or continue the script; or can be predefined errors raise ValueError()

class Custom_error(BaseException):
    pass

try:
    print("hello")
    raise Custom_error
    print("world")
except Custom_error:
    print("found it not stopping now")

print("im outside")

>> hello
>> found it not stopping now
>> im outside

Noticed it didn't stop? We can stop it using just exit(1) in the except block.

2/ Raise can also be used to re-raise the current error to pass it up the stack to see if something else can handle it.

except SomeError, e:
     if not can_handle(e):
          raise
     someone_take_care_of_it(e)

Try/Except blocks:

Does exactly what you think, tries something if an error comes up you catch it and deal with it however you like. No example since there's one above.

Bicarbonate answered 21/10, 2016 at 18:24 Comment(1)
Note, that like Ian Goldby mentioned, assertions are removed on optimization. assert statements are meant as internal consistency checks of the implementation. For errors which might occur during the intended use of your code, raise errors instead.Hellas
U
37

raise - raise an exception.

assert - raise an exception if a given condition is (or isn't) true.

try - execute some code that might raise an exception, and if so, catch it.

Underdrawers answered 21/10, 2016 at 18:15 Comment(0)
B
30

Assertions

  • Should only be used for debugging purposes
  • Although similar to Raise/Exceptions they serve different purposes, because they are useful to point scenarios where program error cannot be recovered from
  • Assertions always raise AssertionError exceptions, here's how they work:

syntax: assert_stmt ::= "assert" expression1 ["," expression2]

at execution time it translates to:

if __debug__:
  if not expression1:
    raise AssertionError(expression2)
  • __debug__ is a built-in flag that is usually true, but if optimisations are triggered it will be false, thus assertions will be dead code => disabled with the -O and -OO flags when starting Python (or PYTHONOPTIMIZE env variable in CPython), so, don't rely on them for code logic.
  • Don’t Use Asserts for Data Validation because of previous point
  • A good use case for assertions => make program "explode" if some unexpected state of the program should make it stop under all circumstances => thus, under circumstances where an exception if caught would make program exit altogether.
  • If you have a bug-free program, then assertions will/should never be triggered, they serve as health checks for the program
  • Careful when using a data structures (such as tuples) as the expression1 in assertions that always evaluate to True for non-empty values => the assertions will always be triggered, breaking down program - eg: assert (<some_test>, 'warn string') => notice the tuple construct (wrong!)

Check: Catching bogus Python asserts on CI by Dan Bader

Raise/Exceptions

  • Their purpose is to handle scenarios where program logic is in an exceptional state but you know what logic to recover from that state
  • When you raise an exception, you can make the type of the exception appropriate to the error (better control over semantic value) and catch it later => so you can create multiple exception types that you know how to recover from, and handle them
  • They are a mechanism for handling known/expected scenarios of run-time errors
  • Useful for data validation when using if-statements and raising validation exceptions per scenario

Try

  • Is just a syntactic element of coding exceptions handling

BTW, I highly recommend the book, "Python Tricks: The Book" by Dan Bader (from realpython.com)

Batting answered 18/1, 2019 at 11:16 Comment(0)
S
23

try/except blocks let you catch and manage exceptions. Exceptions can be triggered by raise, assert, and a large number of errors such as trying to index an empty list. raise is typically used when you have detected an error condition. assert is similar but the exception is only raised if a condition is met.

raise and assert have a different philosophy. There are many "normal" errors in code that you detect and raise errors on. Perhaps a web site doesn't exist or a parameter value is out of range.

Assertions are generally reserved for "I swear this cannot happen" issues that seem to happen anyway. Its more like runtime debugging than normal runtime error detection. Assertions can be disabled if you use the -O flag or run from .pyo files instead of .pyc files, so they should not be part of regular error detection.

If production quality code raises an exception, then figure out what you did wrong. If it raises an AssertionError, you've got a bigger problem.

Shafer answered 21/10, 2016 at 18:22 Comment(0)
C
10

When they are in place, there is no difference between assert and raise AssertionError, they will compile to the exact same bytecode:

import dis

def foo1(param):
    assert param, "fail"

def foo2(param):
    if not param:
        raise AssertionError("fail")

dis.dis(foo1)
print("*" * 60)
dis.dis(foo2)

Output:

 4           0 LOAD_FAST                0 (param)
             2 POP_JUMP_IF_TRUE        12
             4 LOAD_GLOBAL              0 (AssertionError)
             6 LOAD_CONST               1 ('fail')
             8 CALL_FUNCTION            1
            10 RAISE_VARARGS            1
       >>   12 LOAD_CONST               0 (None)
            14 RETURN_VALUE
************************************************************
 7           0 LOAD_FAST                0 (param)
             2 POP_JUMP_IF_TRUE        12

 8           4 LOAD_GLOBAL              0 (AssertionError)
             6 LOAD_CONST               1 ('fail')
             8 CALL_FUNCTION            1
            10 RAISE_VARARGS            1
       >>   12 LOAD_CONST               0 (None)
            14 RETURN_VALUE

But keep i mind that assert stataments will be disabled when running Python with the -O or -OO flags, this is not the case with any raise statements.

Coaptation answered 21/8, 2019 at 8:51 Comment(1)
I believe this is not the case when the -O optimizer flag is used. In that scenario raise AssertionError still works, but assert is ignoredPicrite
Z
1

Exceptions are what Python (and some other languages) use to deal with errors that arise when executing code. raise ExceptionName is saying that there is an error in the code, and specifies what kind of problem it is by raising the Exception associated with that problem. assert expression evaluate expression and raises an Exception if it is false.

try is used to execute code that might raise an Exception that you're expecting. Instead of stopping the program, you can "catch" the exception and deal with it in your code.

Example: Let's say that you have a dictionary and a list. You want to look things from the list in the dictionary until you reach one that isn't in the dictionary:

try:
    for item in my_list:
        print(my_dictionary[item])
except KeyError as e: #KeyError is the Exception raised when a key is not in a dictionary
    print('There is no {} in the dictionary'.format(e.args[0]))
Zimmermann answered 21/10, 2016 at 18:19 Comment(0)
B
1

Assert is generally used by testing code to make sure that something worked:

def test_bool():
    assert True != False

Where as try, raise and except makeup exception handling which is the preferred way in python to handle and propagate errors.

Most libraries and the python built-ins will raise and Exception of one type or another if something goes wrong. Often in you own code you will also want to raise an exception when you detect something going wrong. Let's say as an example you were writing an email address validator and you wanted to raise an exception if the address didn't contain an @ sign. you could have something like (This is toy code, don't actually validate emails like this):

def validate_email(address):
    if not "@" in address:
        raise ValueError("Email Addresses must contain @ sign")

Then somewhere else in your code you can call the validate_email function and if it fails an exception will be thrown.

try:
    validate_email("Mynameisjoe.com")
except ValueError as ex:
    print("We can do some special invalid input handling here, Like ask the user to retry the input")
finally:
    close_my_connection()
    print("Finally always runs whether we succeed or not. Good for clean up like shutting things down.")

The important thing to know is that when an exception is raised it gets passed up the call stack until it finds a handler. If it never finds a handler then it will crash the program with the exception and the stack trace.

One thing you don't want to do is something like:

if __name__ == '__main__':
    try:
        print(1/0)
    except Exception as ex:
        pass

Now you have no way of knowing why your application blew up.

One thing you will see often which is ok is something like:

import logging
if __name__ == '__main__':
    try:
        print(1/0)
    except Exception as ex:
        logging.exception(ex)
        raise

The raise in this case since it has no parameters re-raises the same error. Often in web code you will see something similar that does not re-raise the exception because it will send the 500 error to the client and then carry on with the next request, so in that case you don't want the program to end.

Bove answered 21/10, 2016 at 18:35 Comment(0)
P
1

The other answers explain the differences pretty well, but many fail to mention that assert statements are ignored when the -O optimizer flag is used.

One option to get similarly concise syntax to assert, still have the exceptions take effect when -O is in use, and get the benefits of being able to raise specific exception types is to define a utility function like this:

def raiseif(cond, msg="", exc=AssertionError):
    if cond:
        raise exc(msg)

raiseif(x != y, "x should equal y")

The logic is inverted from assert but you can easily change that if you want.

Picrite answered 23/3, 2020 at 1:23 Comment(2)
Read my answer above (https://mcmap.net/q/211117/-what-39-s-the-difference-between-raise-try-and-assert) and you'll realize this was actually already mentioned and explained quite well.Batting
Another possibility is to create a function called assert_. The inverted case is already mentioned in this answer--the reason that I comment is for how to name the new function.Barefaced

© 2022 - 2024 — McMap. All rights reserved.