The Problem
A common Python mistake: a developer writes list = [101, 102, 103] early in a script, then tries to call list(some_tuple) later. Python throws TypeError: 'list' object is not callable. The built-in list() function hasn't broken—it's been shadowed by a variable name.
Why This Happens
Python's name resolution follows LEGB order: Local, Enclosing, Global, Built-in. When you create a global variable named list, it sits above the built-in list() constructor in the lookup hierarchy. Python finds your variable first and stops searching.
This applies to all 69 built-ins: str, dict, id, max, min. The language permits shadowing because some frameworks need to override built-in behavior. That flexibility creates footguns for everyone else.
Enterprise Impact
In data pipelines and ML workflows, shadowing causes runtime failures that unit tests often miss. A variable named id in one module can break database query builders three imports away. The pattern appears in roughly 5-10% of repositories without linter enforcement.
Modern linters catch this automatically:
- Ruff: Rule A001 (
avoid-builtin-shadow) - Pylint:
redefined-builtin(W0622) - Flake8: Requires
flake8-builtinsplugin (N815) - Mypy: Detects via type checking when builtins are reassigned
Ruff is fastest and catches the same issues as Pylint with better performance. Flake8 requires additional configuration.
The Fix
Three options:
- Rename variables:
listbecomesid_listorcustomer_ids - Delete the shadow:
del listrestores the built-in (temporary fix) - Use
__builtins__: Call__builtins__.list()explicitly (rarely needed)
Enforce option one. Descriptive names prevent shadowing and improve code clarity. Configure your CI pipeline to reject PRs that fail linter checks for built-in shadowing.
CI Configuration
Add Ruff to your pipeline (example for GitHub Actions):
ruff check --select A001
Or Pylint:
pylint --disable=all --enable=redefined-builtin
Both integrate with pre-commit hooks. Ruff is 10-100x faster on large codebases.
What to Watch
No recent developments—this remains a fundamental Python behavior. The rise of AI code generation tools may increase shadowing incidents, as LLMs sometimes suggest list or dict as variable names in example code. Your linting rules matter more than ever.
Python 3.13+ hasn't changed LEGB resolution. This pattern will persist until your team enforces naming standards.