# Errors and Exceptions

Errors and Exceptions in Python are mechanisms for handling issues that arise during program execution. Errors indicate code issues that prevent successful execution, while exceptions are events that disrupt the normal flow of code.

## Error types

### Syntax error

Errors where the code is not valid in Python.

```python
a, b = 1, 2
if a < b
    print("Messge")
```

**Output :**

```python
File "main.py", line 2
    if a < b
           ^
SyntaxError: invalid syntax
```

### Semantic error

Semantic errors can lead to incorrect program behavior or unexpected results, even if the code runs without any exceptions

```python
# Calculate the area of a circle
radius = 5
area = 3.14 * radius * radius
print(area)# output = 78.5
```

In this code snippet, the intention is to calculate the area of a circle using the formula `pi * radius * radius`. However, there is a semantic error in the calculation. Instead of using the accurate value of π (pi), an approximation of `3.14` is used.

```python
import math

radius = 5
area = math.pi * radius * radius
print(area)# output = 78.53981633974483
```

### Runtime errors

* Runtime errors are errors that occur while executing code at runtime.
    
* They are caused by issues like dividing by zero, using undefined variables, performing operations on incompatible types, accessing invalid list indices, etc.
    
* You can handle runtime errors using try/except blocks.
    
* The try block contains the code that may raise an error, and the except block handles the error.
    
    **To catch a** **ZeroDivisionError**:
    
    ```python
    try:  
        num / 0  
    except ZeroDivisionError:
        print("Cannot divide by zero")
    ```
    
    **Catching TypeError:**
    
    ```python
    num = 5
    name = "nirmal"
    try:
         value = num + name  
    except TypeError:
         print("Incompatible types")
    ```
    
    **To catch an IndexError:**
    
    ```python
    try:
        list[invalid_index]  
    except IndexError:
        print("Invalid list index")
    ```
    

## try…except…else…finally

* In addition to try and except, you can use the else and finally keywords to further tune your code’s handling of exceptions.
    
* `else`: The optional `else` block is executed if no exceptions occur in the `try` block. It is used to specify code that should run only when no exceptions are raised.
    
* `finally`: The optional `finally` block is always executed, regardless of whether an exception occurred or not. It is used to specify code that should run regardless of the outcome, such as cleanup operations or resource release.
    
    ```python
    try:
        print("write some logic")
    except:
        print("this happens only if it fails")
    else:
        print("this happens only if it succeeds")
    finally:
        print("this happens no matter what")
    ```
    

## Own Exception Classes

* In python, `raise` keyword is used to explicitly raise an exception or an error during the execution of a program.
    
* It allows you to create custom exceptions or raise built-in exceptions to handle specific scenarios or error conditions.
    
* Syntax : **raise ExceptionType("Error message")**
    
    ```python
    n = int(input("Enter any value::"))
    if n < 0:
      raise ValueError("Value cannot be negative")
    else:
      print(f"Enter value is :: {n}")
    ```
