Understanding Variable Shadowing and Built-ins.
Timothy stared at his screen, his brow furrowed. "Margaret? I think I actually broke Python this time."
Margaret looked up from her tea. "That is a bold claim, Timothy. What makes you say that?"
"It’s the list() function," Timothy said, pointing to his monitor. "I’ve used it a thousand times to convert data. But suddenly, Python is telling me it doesn’t work anymore. It says a list is ‘not callable’."
He showed her the code.
# Timothy's Script to process customer IDs
# Step 1: Create a list of current IDs
list = [101, 102, 103]
print(f"Current IDs: {list}")
# Step 2: Convert a tuple of new IDs into a list
new_ids_tuple = (201, 202, 203)
processed_ids = list(new_ids_tuple) # <--- CRASH!
print(f"Processed...
Understanding Variable Shadowing and Built-ins.
Timothy stared at his screen, his brow furrowed. "Margaret? I think I actually broke Python this time."
Margaret looked up from her tea. "That is a bold claim, Timothy. What makes you say that?"
"It’s the list() function," Timothy said, pointing to his monitor. "I’ve used it a thousand times to convert data. But suddenly, Python is telling me it doesn’t work anymore. It says a list is ‘not callable’."
He showed her the code.
# Timothy's Script to process customer IDs
# Step 1: Create a list of current IDs
list = [101, 102, 103]
print(f"Current IDs: {list}")
# Step 2: Convert a tuple of new IDs into a list
new_ids_tuple = (201, 202, 203)
processed_ids = list(new_ids_tuple) # <--- CRASH!
print(f"Processed: {processed_ids}")
Output:
Current IDs: [101, 102, 103]
TypeError: 'list' object is not callable
"See?" Timothy argued. "I am trying to use list() to convert that tuple. But Python says the list object is not callable. Since when can I not call the list function?"
The Sticker Over the Tool
Margaret smiled gently. "Python is working perfectly, Timothy. The problem is that you borrowed its name tag."
She walked to the whiteboard and wrote four letters: L.E.G.B.
"When you ask for a name, Python looks for it in a specific order," she explained.
- Local Scope (Inside the function)
- Enclosing Scope (Nested functions)
- Global Scope (The main script)
- Built-in Scope (Python’s core tools)
"Names like list, str, print, and max live in the Built-in Scope—the bottom of the toolbox."
"But," she continued, "Python allows you to create variables in the Global Scope. When you wrote list = [101, 102, 103], you stuck a new label named list onto a bag of numbers right on top of your workbench."
"So when the code reached Step 2," Timothy realized, "and I tried to call list(new_ids_tuple)..."
"Python looked for the name list," Margaret finished. "It found your Global bag of numbers first. It stopped looking. And since a bag of numbers is not a function, you can’t ‘call’ it."
Timothy sighed. "Why does Python even allow me to do that? Why not just forbid me from using the word list?"
"Because Python assumes you are a consenting adult," Margaret said. "Sometimes, advanced users need to override built-in behavior for testing or special frameworks. Python gives you the power to change anything—even if that means you occasionally trip over your own shoelaces."
Clearing the Shadow
"So I just shadowed the tool with my own variable," Timothy said.
"Exactly," Margaret said. "This is called Variable Shadowing. To fix it, we simply need to give your variable a better, more descriptive name."
She refactored the code to be safer.
# Margaret's Fix: Descriptive Naming
# Step 1: Use a descriptive name, not a reserved word
current_ids = [101, 102, 103]
print(f"Current IDs: {current_ids}")
# Step 2: Now 'list' still refers to the built-in command
new_ids_tuple = (201, 202, 203)
processed_ids = list(new_ids_tuple) # Works perfectly!
print(f"Processed: {processed_ids}")
Output:
Current IDs: [101, 102, 103]
Processed: [201, 202, 203]
"Much better," Timothy said. "And current_ids actually tells me what the data is, rather than just what type it is."
Margaret’s Cheat Sheet
Margaret opened her notebook to the "Naming Conventions" section.
- The Trap: Naming a variable
list,str,dict,max, orid. - The Symptom: You get a
TypeError: 'list' object is not callablelater in your script. - The Cause: Shadowing. Your variable overwrites the reference to the built-in Python tool because of the LEGB rule.
- The Fix: Rename your variable to be descriptive.
- Bad:
str = "Hello",max = 10
Good: greeting = "Hello", max_score = 10
Quick Check: If your variable name turns a special color in your code editor (usually purple or blue), pick a different name!
Timothy made a note. "I feel bad for blaming Python now. It was doing exactly what I told it to do."
"It usually is," Margaret smiled. "That is both its greatest strength and its greatest frustration."
In the next episode, Margaret and Timothy will face "The Silent Type"—where Timothy learns why comparing a string "5" to the number 5 fails silently in some contexts, or crashes in others.
Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.