09 - Learn Debugging & Best Practices in Python


Introduction

Debugging is an essential skill for writing reliable code, and following best practices ensures clean, efficient, and maintainable Python programs. This step will guide you through debugging techniques and Python coding standards.



1. Debugging Techniques


Using Print Statements

def add_numbers(a, b):
    print(f"Adding {a} and {b}")
    return a + b

result = add_numbers(5, 3)
print("Result:", result)

Best for: Quick debugging but should not be used in production code.


Using the Python Debugger (pdb)

import pdb

def divide(a, b):
    pdb.set_trace()  # Debugger starts here
    return a / b

divide(10, 2)

Commands in pdb:

  • n (next line)
  • s (step into function)
  • c (continue execution)
  • q (quit debugger)

Using Logging for Debugging

pip install logging
import logging
logging.basicConfig(level=logging.DEBUG)

def multiply(a, b):
    logging.debug(f"Multiplying {a} and {b}")
    return a * b

multiply(4, 5)

Best for: Production debugging and logging system events.



2. Handling Errors Gracefully


Using Try-Except Blocks

try:
    x = int(input("Enter a number: "))
    result = 10 / x
except ValueError:
    print("Invalid input. Please enter a number.")
except ZeroDivisionError:
    print("Cannot divide by zero.")
else:
    print("Result:", result)
finally:
    print("Execution completed.")

Best for: Handling known errors and preventing program crashes.



3. Best Practices in Python


1. Follow PEP 8 (Python Style Guide)

  • Use 4 spaces per indentation level (avoid tabs).
  • Keep lines under 79 characters long.
  • Use meaningful variable names.
  • Add comments where necessary.

2. Use List Comprehensions for Efficient Code

squares = [x**2 for x in range(10)]
print(squares)

Best for: Writing concise and readable code.


3. Avoid Mutable Default Arguments

def add_item(item, my_list=None):
    if my_list is None:
        my_list = []
    my_list.append(item)
    return my_list

Why? Default arguments are shared between function calls.


4. Use F-Strings for String Formatting (Python 3.6+)

name = "Alice"
age = 25
print(f"My name is {name} and I am {age} years old.")

Best for: Improving readability and performance.


5. Optimize Performance with Generators

def generate_numbers():
    for i in range(5):
        yield i

for num in generate_numbers():
    print(num)

Best for: Efficient memory usage in large datasets.



Exercises

  • Debug a function using pdb and analyze step-by-step execution.
  • Implement logging in a script instead of print statements.
  • Write a function with try-except-finally to handle multiple exceptions.
  • Refactor a piece of code using list comprehensions.
  • Optimize a function using a generator instead of returning a list.


Conclusion

You have now learned debugging techniques, error handling, and Python best practices to write cleaner and more efficient code. The next step is to build a real-world project and contribute to open source.


NoFuture - A new way to learn it stuff

Sn0wAlice

NoFuture Menthor - Cybersec Analyst

I'm Alice Snow, a cybersecurity professional with a passion for Blue Team operations, defensive security, and compliance. I focus on creating practical solutions to help organizations strengthen their security posture. I’m also involved in offensive CI/CD research and incident detection, always looking for ways to bridge the gap between security theory and real-world application.

Profile Profile