Difference between Raise Try and Assert

你说的曾经没有我的故事 提交于 2019-11-29 23:06:08

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 that is usefull for this:

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 contiune 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 reraise 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 how ever you like. No example since there's one above.

assert cond, "text"

is expanded to something like

if cond == False:
  raise AssertionError("text")

use assert because it is more readable.

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.

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.

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]))

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.

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)

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")

print(dis.dis(foo1))
print(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
None
 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
None
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!