杰瑞科技汇

Python BuggyModule 如何快速定位与修复BUG?

Of course! This is a fantastic and classic way to learn about Python's core mechanics. A "buggy module" is an excellent tool for understanding how Python resolves names, imports, and the difference between code running at import time versus runtime.

Python BuggyModule 如何快速定位与修复BUG?-图1
(图片来源网络,侵删)

Let's create a step-by-step guide to building, using, and understanding a buggy module.

The Goal

We will create a Python module named buggymodule.py that is intentionally full of common Python bugs. Then, we'll create a main script, main.py, that imports and uses this module to see the errors in action.


Step 1: Create the Buggy Module (buggymodule.py)

This module will contain a variety of errors, from simple typos to more subtle logical issues.

Create a file named buggymodule.py and paste the following code into it. I've added comments explaining the bug in each section.

Python BuggyModule 如何快速定位与修复BUG?-图2
(图片来源网络,侵删)
# buggymodule.py
# =====================================================================
# BUG 1: NameError at Import Time
# =====================================================================
# This code runs as soon as the module is imported.
# The variable 'some_other_variable' is not defined, causing a NameError.
print("--- BUG 1: NameError at Import Time ---")
# print(some_other_variable)  # <-- UNCOMMENT THIS LINE TO SEE THE ERROR
# To fix it, either comment it out or define the variable first.
some_other_variable = "This is defined now."
print(f"BUG 1 is fixed. The variable is: {some_other_variable}\n")
# =====================================================================
# BUG 2: AttributeError
# =====================================================================
# This function tries to use a method that doesn't exist on a string object.
def process_string(text):
    """This function has a bug in it."""
    print(f"--- BUG 2: AttributeError in process_string ---")
    # The method '.upppercase()' does not exist. It should be '.upper()'.
    # This error will only happen when the function is *called*, not on import.
    return text.upppercase() # <-- CHANGE TO .upper() TO FIX
# Let's call the buggy function to see the error.
# process_string("hello") # <-- UNCOMMENT THIS LINE TO SEE THE ERROR
print("BUG 2 is fixed by changing 'upppercase' to 'upper'.\n")
# =====================================================================
# BUG 3: Logical Error (No Exception!)
# =====================================================================
# This function has a logical mistake. It runs without crashing but gives
# the wrong answer. This is often the hardest type of bug to find.
def add_numbers(a, b):
    """This function incorrectly adds numbers."""
    print(f"--- BUG 3: Logical Error in add_numbers ---")
    # The logic is wrong. It should be `return a + b`.
    return a * b # <-- CHANGE TO + TO FIX
# Let's test the buggy function.
# print(f"The buggy result of 5 + 3 is: {add_numbers(5, 3)}") # <-- UNCOMMENT TO SEE
print("BUG 3 is fixed by changing '*' to '+'.\n")
# =====================================================================
# BUG 4: IndexError
# =====================================================================
# This function tries to access a list index that is out of bounds.
def get_third_item(items):
    """This function will crash if the list has less than 3 items."""
    print(f"--- BUG 4: IndexError in get_third_item ---")
    # If 'items' has 0, 1, or 2 elements, index 2 will not exist.
    return items[2] # <-- This will raise an IndexError if len(items) < 3
# Let's call it with a list that is too small.
# short_list = ['a', 'b']
# print(f"The third item is: {get_third_item(short_list)}") # <-- UNCOMMENT TO SEE
print("BUG 4 can be fixed by checking the list length first.\n")
# =====================================================================
# BUG 5: Module-Level Name Shadowing
# =====================================================================
# This is a subtle bug where a variable name in a function "shadows"
# a variable defined at the module level.
MODULE_CONSTANT = 100
def get_constant():
    """This function accidentally uses a local variable instead of the module one."""
    print(f"--- BUG 5: Name Shadowing in get_constant ---")
    # By assigning to 'MODULE_CONSTANT' inside the function, Python
    # creates a *new local variable* with that name, hiding the global one.
    # This is a common source of bugs.
    MODULE_CONSTANT = 50  # <-- This creates a LOCAL variable, not the global one.
    return MODULE_CONSTANT
# Let's see the result.
# print(f"The returned constant is: {get_constant()}")
# print(f"The module-level constant is still: {MODULE_CONSTANT}") # <-- It's unchanged!
print("BUG 5 is fixed by using the 'global' keyword or a different variable name.\n")
# =====================================================================
# GOOD PRACTICE: Define a "public" API for the module
# =====================================================================
# To make our module useful, we should define what functions it's meant to export.
# A user of our module should only need to import what's necessary.
# We can fix our buggy functions and make them available.
def process_string_fixed(text):
    """The corrected version of process_string."""
    return text.upper()
def add_numbers_fixed(a, b):
    """The corrected version of add_numbers."""
    return a + b
def get_third_item_safe(items):
    """A safe version of get_third_item."""
    if len(items) >= 3:
        return items[2]
    else:
        return None # Return None if the item doesn't exist
# Now, a user of our module can choose to import the safe, fixed functions.
# from buggymodule import process_string_fixed, add_numbers_fixed

Step 2: Create the Main Script (main.py)

This script will import buggymodule and trigger the bugs one by one.

Create a file named main.py in the same directory as buggymodule.py and paste this code in:

# main.py
print("Starting the main script...")
# =====================================================================
# DEMONSTRATING THE BUGS
# =====================================================================
# BUG 1: NameError at Import Time
# If you uncommented the line in buggymodule.py, the import itself will fail here.
try:
    import buggymodule
    print("Successfully imported buggymodule.\n")
except NameError as e:
    print(f"CRITICAL ERROR: Failed to import buggymodule due to a NameError: {e}\n")
# BUG 2: AttributeError (happens at runtime)
# This error only occurs when the buggy function is called.
print("--- Testing BUG 2: AttributeError ---")
try:
    # We are calling the buggy version of the function from the module.
    result = buggymodule.process_string("hello world")
    print(f"Function result: {result}\n")
except AttributeError as e:
    print(f"Caught an AttributeError as expected: {e}\n")
# BUG 3: Logical Error (No Exception!)
# This function runs but produces the wrong output. It won't raise an error.
print("--- Testing BUG 3: Logical Error ---")
buggy_sum = buggymodule.add_numbers(10, 5)
print(f"The buggy sum of 10 + 5 is: {buggy_sum} (This should be 15!)\n")
# BUG 4: IndexError (happens at runtime)
print("--- Testing BUG 4: IndexError ---")
try:
    short_list = ['first', 'second']
    third_item = buggymodule.get_third_item(short_list)
    print(f"The third item is: {third_item}\n")
except IndexError as e:
    print(f"Caught an IndexError as expected: {e}\n")
# BUG 5: Name Shadowing (subtle bug)
print("--- Testing BUG 5: Name Shadowing ---")
# The module's constant value is unchanged by the buggy function.
print(f"The module's constant is: {buggymodule.MODULE_CONSTANT}")
# The buggy function returns the local, incorrect value.
shadowed_value = buggymodule.get_constant()
print(f"The function returned: {shadowed_value} (This is the local shadow variable)\n")
# =====================================================================
# USING THE FIXED FUNCTIONS
# =====================================================================
print("--- Using the FIXED functions from the module ---")
# You can import the fixed functions directly.
from buggymodule import process_string_fixed, add_numbers_fixed, get_third_item_safe
print(f"Fixed string processing: {process_string_fixed('this works now!')}")
print(f"Fixed addition: {add_numbers_fixed(10, 5)}")
safe_list = ['a', 'b']
item = get_third_item_safe(safe_list)
print(f"Getting third item from a short list safely: {item}")
long_list = ['a', 'b', 'c', 'd']
item = get_third_item_safe(long_list)
print(f"Getting third item from a long list safely: {item}")
print("\nScript finished.")

Step 3: Run the Code and Analyze the Output

  1. Make sure both main.py and buggymodule.py are in the same folder.
  2. Open your terminal or command prompt, navigate to that folder.
  3. Run the main script: python main.py

Expected Output (with bugs active):

You will see the output from main.py interspersed with the print statements from buggymodule.py. The try...except blocks will catch the errors and print informative messages.

Starting the main script...
--- BUG 1: NameError at Import Time ---
BUG 1 is fixed. The variable is: This is defined now.
Successfully imported buggymodule.
--- Testing BUG 2: AttributeError ---
--- BUG 2: AttributeError in process_string ---
Caught an AttributeError as expected: 'str' object has no attribute 'upppercase'
--- Testing BUG 3: Logical Error ---
--- BUG 3: Logical Error in add_numbers ---
The buggy sum of 10 + 5 is: 50 (This should be 15!)
--- Testing BUG 4: IndexError ---
--- BUG 4: IndexError in get_third_item ---
Caught an IndexError as expected: list index out of range
--- Testing BUG 5: Name Shadowing ---
--- BUG 5: Name Shadowing in get_constant ---
The module's constant is: 100
The function returned: 50 (This is the local shadow variable)
--- Using the FIXED functions from the module ---
Fixed string processing: THIS WORKS NOW!
Fixed addition: 15
Getting third item from a short list safely: None
Getting third item from a long list safely: c
Script finished.

Key Takeaways

By creating and using buggymodule.py, you've learned:

Python BuggyModule 如何快速定位与修复BUG?-图3
(图片来源网络,侵删)
  1. Import Time vs. Runtime: Code at the top level of a module runs when it's imported. Errors here will stop the import process. Errors inside functions only happen when the function is called.
  2. Different Error Types:
    • NameError: A variable or name is not defined in the current scope.
    • AttributeError: You try to access an attribute or method that an object doesn't have (e.g., "".foo()).
    • IndexError: You try to access a list or tuple index that doesn't exist.
    • Logical Error: The code runs but produces the wrong result. These require careful testing and review.
  3. Variable Scope (Shadowing): A variable defined inside a function is local to that function. If you have a global variable and a local variable with the same name, the local one "shadows" the global one. This is a very common source of bugs.
  4. Module APIs: Good modules provide a clean, predictable set of functions (a public API) for users. By defining fixed, "safe" functions and making them easy to import, you create a more robust and user-friendly library.
分享:
扫描分享到社交APP
上一篇
下一篇