How does Python handle exceptions?

In Python, exceptions are handled using try-except blocks. This mechanism allows you to catch and manage errors that occur during program execution, preventing the program from crashing. Here’s a breakdown of how Python handles exceptions:


1. Basic Structure of Try-Except:

try:
    # Code that might raise an exception
    risky_code()
except ExceptionType:
    # Code to handle the exception
    print("An error occurred.")
  • try block: Contains the code that might throw an exception.
  • except block: Contains the code to handle specific exceptions.

2. Handling Specific Exceptions:

You can handle specific types of exceptions to address them appropriately.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("You can't divide by zero!")

Here, only a ZeroDivisionError will be caught.


3. Catching Multiple Exceptions:

You can handle multiple exceptions either by listing them in a tuple or using multiple except blocks.

try:
    risky_code()
except (ValueError, KeyError) as e:
    print(f"Caught a ValueError or KeyError: {e}")
try:
    risky_code()
except ValueError:
    print("ValueError occurred.")
except KeyError:
    print("KeyError occurred.")

4. Catching All Exceptions:

To catch all exceptions, use except Exception or a bare except (not recommended).

try:
    risky_code()
except Exception as e:
    print(f"An unexpected error occurred: {e}")

5. The Else Clause:

The else block runs if the try block does not raise any exception.

try:
    result = 10 / 2
except ZeroDivisionError:
    print("You can't divide by zero!")
else:
    print(f"Result is {result}")

6. The Finally Clause:

The finally block is used to execute code regardless of whether an exception was raised or not. It’s often used for cleanup operations.

try:
    risky_code()
except Exception:
    print("An error occurred.")
finally:
    print("This will always execute.")

7. Raising Exceptions:

You can raise exceptions explicitly using the raise keyword.

if age < 0:
    raise ValueError("Age cannot be negative!")

8. Custom Exceptions:

You can define custom exceptions by subclassing the Exception class.

class CustomError(Exception):
    pass

try:
    raise CustomError("This is a custom error!")
except CustomError as e:
    print(e)

Summary:

Python’s exception handling mechanism is flexible and powerful, allowing developers to:

  • Anticipate errors.
  • Provide specific responses to different errors.
  • Ensure resources are cleaned up properly.

This promotes robust and maintainable code.

How do you merge two dictionaries in Python?

Merging two dictionaries in Python can be achieved in multiple ways, depending on your Python version and preference. Here’s a breakdown of the most common methods:


1. Using the update() Method (Available in All Versions)

The update() method adds the key-value pairs from one dictionary to another. If keys overlap, the values in the second dictionary overwrite those in the first.

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

dict1.update(dict2)  # Modifies dict1 in place
print(dict1)  # Output: {'a': 1, 'b': 3, 'c': 4}

2. Using the {**dict1, **dict2} Syntax (Python 3.5+)

You can use unpacking to merge dictionaries. This creates a new dictionary without modifying the originals.

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

merged_dict = {**dict1, **dict2}
print(merged_dict)  # Output: {'a': 1, 'b': 3, 'c': 4}

3. Using the | Operator (Python 3.9+)

The | operator provides a concise way to merge dictionaries and returns a new dictionary. This is similar to unpacking but more readable.

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

merged_dict = dict1 | dict2
print(merged_dict)  # Output: {'a': 1, 'b': 3, 'c': 4}

4. Using Dictionary Comprehension

For more control, you can use dictionary comprehension to merge dictionaries manually.

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

merged_dict = {key: value for d in [dict1, dict2] for key, value in d.items()}
print(merged_dict)  # Output: {'a': 1, 'b': 3, 'c': 4}

5. Using a Third-Party Library (collections.ChainMap)

The ChainMap class from the collections module groups multiple dictionaries together. It does not create a new dictionary but provides a single view for lookup.

from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

merged = ChainMap(dict2, dict1)  # dict2 takes precedence for overlapping keys
print(dict(merged))  # Output: {'a': 1, 'b': 3, 'c': 4}

Key Points

  • Use update() if you want to modify an existing dictionary.
  • Use {**dict1, **dict2} or | if you need a new dictionary.
  • The ChainMap approach is efficient for lookups but not for creating a standalone dictionary.

Let me know which method suits your needs, or if you’d like to see an example tailored to your use case!