Trending:
Software Development

Python variable shadowing: why linters flag `list = [1,2,3]` in enterprise code

Naming a variable `list` or `id` breaks Python's built-in functions later in your code. Linters like Ruff and Pylint flag this automatically, but the pattern still appears in 5-10% of unlinted enterprise repositories. The fix is simple: use descriptive names.

Python variable shadowing: why linters flag `list = [1,2,3]` in enterprise code Photo by ANOOF C on Unsplash

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-builtins plugin (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:

  1. Rename variables: list becomes id_list or customer_ids
  2. Delete the shadow: del list restores the built-in (temporary fix)
  3. 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.