Code icon

The App is Under a Quick Maintenance

We apologize for the inconvenience. Please come back later

Menu iconMenu iconPython & SQL Bible
Python & SQL Bible

Chapter 8: Exceptional Python

8.5 Practical Exercises of Chapter 8: Exceptional Python

Exercise 1: Creating a custom exception

Define a new exception class called TooColdError that inherits from the built-in Exception class. Raise this exception in a function called check_temperature that takes a temperature value as an argument and raises TooColdError if the temperature is below 0.

# Exercise 1 skeleton code
class TooColdError(Exception):
    pass

def check_temperature(temp):
    # your code here

# Test your function
try:
    check_temperature(-5)
except TooColdError:
    print("Caught a TooColdError!")

Exercise 2: Adding exception handling

Modify the check_temperature function to handle the case where the argument passed is not a number. If this happens, print a friendly error message and return None.

# Exercise 2 skeleton code
def check_temperature(temp):
    # your code here

# Test your function with a non-number argument
result = check_temperature("hot")

Exercise 3: Logging

Create a logger and use it to log messages of various levels. Then, adjust the logging level of the logger and observe how the logged messages change.

# Exercise 3 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

# Change the logging level and log some more messages
logger.setLevel(logging.ERROR)
# Log the same set of messages and see what changes

Exercise 4: Advanced logging

Set up a logger to log messages to both the console and a file. Try adding a time stamp to the log messages.

# Exercise 4 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Set up console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# Set up file handler
fh = logging.FileHandler("debug.log")
fh.setLevel(logging.DEBUG)

# Add handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

Remember to try to solve the exercises on your own before looking at the solutions!

Chapter 8 Conclusion

Chapter 8, "Exceptional Python", has been a deep dive into Python's tools for handling and reporting errors in your code. From basic error and exception handling to defining custom exceptions and leveraging Python's robust logging library, we've explored a range of techniques that make Python a flexible and powerful language for both developing and debugging applications.

We started the chapter with a discussion on error and exception handling. We learned that Python differentiates between syntax errors and exceptions. Syntax errors occur when Python cannot interpret our code, whereas exceptions occur when syntactically correct Python code runs into an error.

The try/except block was introduced as a way to catch and handle exceptions. The bare except clause may catch all types of exceptions, but it is not a good practice to use it due to its ability to catch unexpected errors and hide programming mistakes. Therefore, it is better to catch exceptions explicitly by their type. We also explored how to utilize the else clause, which executes if the try block did not throw any exception, and finally clause, which executes no matter what, providing a surefire method of cleaning up resources or executing code that absolutely must run.

We then moved on to defining and raising custom exceptions. We discovered that custom exceptions are a powerful tool for creating expressive and self-documenting code. By raising exceptions with names that clearly state what went wrong, and providing relevant details in the exception message, we make our code easier to debug and maintain.

The discussion on Python's logging module showed us the advantages of using logging over print statements. Logging provides a more flexible way to output information about what our program is doing. We can control the level of detail outputted through log levels, direct output to multiple destinations, and format our output messages. The logging module provides a way to handle unexpected situations that don’t necessarily qualify as exceptions.

To sum up, the constructs and libraries we've learned in this chapter are crucial for writing robust, production-quality code in Python. They allow us to handle unforeseen situations gracefully and make debugging and maintenance easier by providing clear and detailed reports about what our code is doing. Mastering these tools is a key step in becoming a proficient Python programmer. In the following chapters, we'll build on these foundations as we start working with external resources like files and databases.

8.5 Practical Exercises of Chapter 8: Exceptional Python

Exercise 1: Creating a custom exception

Define a new exception class called TooColdError that inherits from the built-in Exception class. Raise this exception in a function called check_temperature that takes a temperature value as an argument and raises TooColdError if the temperature is below 0.

# Exercise 1 skeleton code
class TooColdError(Exception):
    pass

def check_temperature(temp):
    # your code here

# Test your function
try:
    check_temperature(-5)
except TooColdError:
    print("Caught a TooColdError!")

Exercise 2: Adding exception handling

Modify the check_temperature function to handle the case where the argument passed is not a number. If this happens, print a friendly error message and return None.

# Exercise 2 skeleton code
def check_temperature(temp):
    # your code here

# Test your function with a non-number argument
result = check_temperature("hot")

Exercise 3: Logging

Create a logger and use it to log messages of various levels. Then, adjust the logging level of the logger and observe how the logged messages change.

# Exercise 3 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

# Change the logging level and log some more messages
logger.setLevel(logging.ERROR)
# Log the same set of messages and see what changes

Exercise 4: Advanced logging

Set up a logger to log messages to both the console and a file. Try adding a time stamp to the log messages.

# Exercise 4 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Set up console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# Set up file handler
fh = logging.FileHandler("debug.log")
fh.setLevel(logging.DEBUG)

# Add handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

Remember to try to solve the exercises on your own before looking at the solutions!

Chapter 8 Conclusion

Chapter 8, "Exceptional Python", has been a deep dive into Python's tools for handling and reporting errors in your code. From basic error and exception handling to defining custom exceptions and leveraging Python's robust logging library, we've explored a range of techniques that make Python a flexible and powerful language for both developing and debugging applications.

We started the chapter with a discussion on error and exception handling. We learned that Python differentiates between syntax errors and exceptions. Syntax errors occur when Python cannot interpret our code, whereas exceptions occur when syntactically correct Python code runs into an error.

The try/except block was introduced as a way to catch and handle exceptions. The bare except clause may catch all types of exceptions, but it is not a good practice to use it due to its ability to catch unexpected errors and hide programming mistakes. Therefore, it is better to catch exceptions explicitly by their type. We also explored how to utilize the else clause, which executes if the try block did not throw any exception, and finally clause, which executes no matter what, providing a surefire method of cleaning up resources or executing code that absolutely must run.

We then moved on to defining and raising custom exceptions. We discovered that custom exceptions are a powerful tool for creating expressive and self-documenting code. By raising exceptions with names that clearly state what went wrong, and providing relevant details in the exception message, we make our code easier to debug and maintain.

The discussion on Python's logging module showed us the advantages of using logging over print statements. Logging provides a more flexible way to output information about what our program is doing. We can control the level of detail outputted through log levels, direct output to multiple destinations, and format our output messages. The logging module provides a way to handle unexpected situations that don’t necessarily qualify as exceptions.

To sum up, the constructs and libraries we've learned in this chapter are crucial for writing robust, production-quality code in Python. They allow us to handle unforeseen situations gracefully and make debugging and maintenance easier by providing clear and detailed reports about what our code is doing. Mastering these tools is a key step in becoming a proficient Python programmer. In the following chapters, we'll build on these foundations as we start working with external resources like files and databases.

8.5 Practical Exercises of Chapter 8: Exceptional Python

Exercise 1: Creating a custom exception

Define a new exception class called TooColdError that inherits from the built-in Exception class. Raise this exception in a function called check_temperature that takes a temperature value as an argument and raises TooColdError if the temperature is below 0.

# Exercise 1 skeleton code
class TooColdError(Exception):
    pass

def check_temperature(temp):
    # your code here

# Test your function
try:
    check_temperature(-5)
except TooColdError:
    print("Caught a TooColdError!")

Exercise 2: Adding exception handling

Modify the check_temperature function to handle the case where the argument passed is not a number. If this happens, print a friendly error message and return None.

# Exercise 2 skeleton code
def check_temperature(temp):
    # your code here

# Test your function with a non-number argument
result = check_temperature("hot")

Exercise 3: Logging

Create a logger and use it to log messages of various levels. Then, adjust the logging level of the logger and observe how the logged messages change.

# Exercise 3 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

# Change the logging level and log some more messages
logger.setLevel(logging.ERROR)
# Log the same set of messages and see what changes

Exercise 4: Advanced logging

Set up a logger to log messages to both the console and a file. Try adding a time stamp to the log messages.

# Exercise 4 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Set up console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# Set up file handler
fh = logging.FileHandler("debug.log")
fh.setLevel(logging.DEBUG)

# Add handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

Remember to try to solve the exercises on your own before looking at the solutions!

Chapter 8 Conclusion

Chapter 8, "Exceptional Python", has been a deep dive into Python's tools for handling and reporting errors in your code. From basic error and exception handling to defining custom exceptions and leveraging Python's robust logging library, we've explored a range of techniques that make Python a flexible and powerful language for both developing and debugging applications.

We started the chapter with a discussion on error and exception handling. We learned that Python differentiates between syntax errors and exceptions. Syntax errors occur when Python cannot interpret our code, whereas exceptions occur when syntactically correct Python code runs into an error.

The try/except block was introduced as a way to catch and handle exceptions. The bare except clause may catch all types of exceptions, but it is not a good practice to use it due to its ability to catch unexpected errors and hide programming mistakes. Therefore, it is better to catch exceptions explicitly by their type. We also explored how to utilize the else clause, which executes if the try block did not throw any exception, and finally clause, which executes no matter what, providing a surefire method of cleaning up resources or executing code that absolutely must run.

We then moved on to defining and raising custom exceptions. We discovered that custom exceptions are a powerful tool for creating expressive and self-documenting code. By raising exceptions with names that clearly state what went wrong, and providing relevant details in the exception message, we make our code easier to debug and maintain.

The discussion on Python's logging module showed us the advantages of using logging over print statements. Logging provides a more flexible way to output information about what our program is doing. We can control the level of detail outputted through log levels, direct output to multiple destinations, and format our output messages. The logging module provides a way to handle unexpected situations that don’t necessarily qualify as exceptions.

To sum up, the constructs and libraries we've learned in this chapter are crucial for writing robust, production-quality code in Python. They allow us to handle unforeseen situations gracefully and make debugging and maintenance easier by providing clear and detailed reports about what our code is doing. Mastering these tools is a key step in becoming a proficient Python programmer. In the following chapters, we'll build on these foundations as we start working with external resources like files and databases.

8.5 Practical Exercises of Chapter 8: Exceptional Python

Exercise 1: Creating a custom exception

Define a new exception class called TooColdError that inherits from the built-in Exception class. Raise this exception in a function called check_temperature that takes a temperature value as an argument and raises TooColdError if the temperature is below 0.

# Exercise 1 skeleton code
class TooColdError(Exception):
    pass

def check_temperature(temp):
    # your code here

# Test your function
try:
    check_temperature(-5)
except TooColdError:
    print("Caught a TooColdError!")

Exercise 2: Adding exception handling

Modify the check_temperature function to handle the case where the argument passed is not a number. If this happens, print a friendly error message and return None.

# Exercise 2 skeleton code
def check_temperature(temp):
    # your code here

# Test your function with a non-number argument
result = check_temperature("hot")

Exercise 3: Logging

Create a logger and use it to log messages of various levels. Then, adjust the logging level of the logger and observe how the logged messages change.

# Exercise 3 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

# Change the logging level and log some more messages
logger.setLevel(logging.ERROR)
# Log the same set of messages and see what changes

Exercise 4: Advanced logging

Set up a logger to log messages to both the console and a file. Try adding a time stamp to the log messages.

# Exercise 4 skeleton code
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Set up console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# Set up file handler
fh = logging.FileHandler("debug.log")
fh.setLevel(logging.DEBUG)

# Add handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

# Log some messages
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

Remember to try to solve the exercises on your own before looking at the solutions!

Chapter 8 Conclusion

Chapter 8, "Exceptional Python", has been a deep dive into Python's tools for handling and reporting errors in your code. From basic error and exception handling to defining custom exceptions and leveraging Python's robust logging library, we've explored a range of techniques that make Python a flexible and powerful language for both developing and debugging applications.

We started the chapter with a discussion on error and exception handling. We learned that Python differentiates between syntax errors and exceptions. Syntax errors occur when Python cannot interpret our code, whereas exceptions occur when syntactically correct Python code runs into an error.

The try/except block was introduced as a way to catch and handle exceptions. The bare except clause may catch all types of exceptions, but it is not a good practice to use it due to its ability to catch unexpected errors and hide programming mistakes. Therefore, it is better to catch exceptions explicitly by their type. We also explored how to utilize the else clause, which executes if the try block did not throw any exception, and finally clause, which executes no matter what, providing a surefire method of cleaning up resources or executing code that absolutely must run.

We then moved on to defining and raising custom exceptions. We discovered that custom exceptions are a powerful tool for creating expressive and self-documenting code. By raising exceptions with names that clearly state what went wrong, and providing relevant details in the exception message, we make our code easier to debug and maintain.

The discussion on Python's logging module showed us the advantages of using logging over print statements. Logging provides a more flexible way to output information about what our program is doing. We can control the level of detail outputted through log levels, direct output to multiple destinations, and format our output messages. The logging module provides a way to handle unexpected situations that don’t necessarily qualify as exceptions.

To sum up, the constructs and libraries we've learned in this chapter are crucial for writing robust, production-quality code in Python. They allow us to handle unforeseen situations gracefully and make debugging and maintenance easier by providing clear and detailed reports about what our code is doing. Mastering these tools is a key step in becoming a proficient Python programmer. In the following chapters, we'll build on these foundations as we start working with external resources like files and databases.