python-gotchas

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Quick Reference

快速参考

GotchaProblemFix
Mutable default
def f(x=[])
Use
None
, create in function
Iterate + mutateSkips itemsIterate over copy
items[:]
is
vs
==
Identity vs valueUse
is
only for
None
Late binding
lambda: i
captures var
lambda i=i: i
Float precision
0.1 + 0.2 != 0.3
math.isclose()
Dict mutationRuntimeError
list(d.keys())
Class attributeShared mutableInit in
__init__
Falsy ValuesExamples
Boolean
False
None
None
Numbers
0
,
0.0
,
0j
Empty collections
""
,
[]
,
{}
,
set()
Scope RuleOrder
LEGBLocal → Enclosing → Global → Built-in
global
Access module-level variable
nonlocal
Access enclosing function variable
陷阱类型问题修复方案
Mutable default
def f(x=[])
使用
None
,在函数内部创建
Iterate + mutate跳过元素迭代副本
items[:]
is
vs
==
身份标识 vs 值仅对
None
使用
is
Late binding
lambda: i
捕获变量
lambda i=i: i
Float precision
0.1 + 0.2 != 0.3
math.isclose()
Dict mutation运行时错误
list(d.keys())
Class attribute可变属性共享
__init__
中初始化
假值示例
布尔值
False
None
None
数字
0
,
0.0
,
0j
空集合
""
,
[]
,
{}
,
set()
作用域规则优先级
LEGB局部 → 嵌套 → 全局 → 内置
global
访问模块级变量
nonlocal
访问嵌套函数变量

When to Use This Skill

何时使用本指南

Use for debugging and prevention:
  • Understanding why code behaves unexpectedly
  • Avoiding common Python pitfalls
  • Reviewing code for subtle bugs
  • Learning Python's evaluation rules
  • Fixing mutable default arguments
Related skills:
  • For fundamentals: see
    python-fundamentals-313
  • For testing: see
    python-testing
  • For type hints: see
    python-type-hints

用于调试和预防场景:
  • 理解代码行为异常的原因
  • 避免常见Python陷阱
  • 检查代码中的潜在bug
  • 学习Python的求值规则
  • 修复可变默认参数问题
相关技能:
  • 基础内容:查看
    python-fundamentals-313
  • 测试内容:查看
    python-testing
  • 类型提示:查看
    python-type-hints

Python Common Gotchas and Pitfalls

Python常见陷阱与误区

Overview

概述

Python has several well-known pitfalls that trip up developers of all experience levels. Understanding these gotchas prevents subtle bugs and unexpected behavior.
Python存在一些广为人知的陷阱,会困扰各个经验水平的开发者。了解这些陷阱可以避免潜在的bug和意外行为。

1. Mutable Default Arguments

1. Mutable Default Arguments

The Problem

问题

python
undefined
python
undefined

BAD: Mutable default argument

BAD: Mutable default argument

def add_item(item, items=[]): items.append(item) return items
def add_item(item, items=[]): items.append(item) return items

Unexpected behavior!

Unexpected behavior!

print(add_item("a")) # ['a'] print(add_item("b")) # ['a', 'b'] - NOT ['b']! print(add_item("c")) # ['a', 'b', 'c']
undefined
print(add_item("a")) # ['a'] print(add_item("b")) # ['a', 'b'] - NOT ['b']! print(add_item("c")) # ['a', 'b', 'c']
undefined

Why It Happens

原因

Default arguments are evaluated once when the function is defined, not each time it's called. The same list object is reused across all calls.
默认参数在函数定义时仅求值一次,而非每次调用时求值。同一个列表对象会在所有调用中被复用。

The Fix

修复方案

python
undefined
python
undefined

GOOD: Use None as default

GOOD: Use None as default

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

Works correctly

Works correctly

print(add_item("a")) # ['a'] print(add_item("b")) # ['b'] print(add_item("c")) # ['c']
undefined
print(add_item("a")) # ['a'] print(add_item("b")) # ['b'] print(add_item("c")) # ['c']
undefined

Other Mutable Defaults

其他可变默认值

python
undefined
python
undefined

BAD: All mutable types have this issue

BAD: All mutable types have this issue

def bad_dict(data={}): ... def bad_set(data=set()): ... def bad_class(config=SomeClass()): ...
def bad_dict(data={}): ... def bad_set(data=set()): ... def bad_class(config=SomeClass()): ...

GOOD: Always use None

GOOD: Always use None

def good_dict(data=None): if data is None: data = {} return data
def good_set(data=None): if data is None: data = set() return data
undefined
def good_dict(data=None): if data is None: data = {} return data
def good_set(data=None): if data is None: data = set() return data
undefined

2. Mutating Lists While Iterating

2. Mutating Lists While Iterating

The Problem

问题

python
undefined
python
undefined

BAD: Modifying list during iteration

BAD: Modifying list during iteration

numbers = [1, 2, 3, 4, 5, 6] for num in numbers: if num % 2 == 0: numbers.remove(num)
print(numbers) # [1, 3, 5] - missed 4!
undefined
numbers = [1, 2, 3, 4, 5, 6] for num in numbers: if num % 2 == 0: numbers.remove(num)
print(numbers) # [1, 3, 5] - missed 4!
undefined

Why It Happens

原因

The iterator uses indices internally. When you remove an item, all subsequent indices shift, causing items to be skipped.
迭代器内部使用索引。当删除元素时,后续所有索引都会偏移,导致元素被跳过。

The Fixes

修复方案

python
undefined
python
undefined

GOOD: Iterate over a copy

GOOD: Iterate over a copy

numbers = [1, 2, 3, 4, 5, 6] for num in numbers[:]: # Slice creates a copy if num % 2 == 0: numbers.remove(num) print(numbers) # [1, 3, 5]
numbers = [1, 2, 3, 4, 5, 6] for num in numbers[:]: # Slice creates a copy if num % 2 == 0: numbers.remove(num) print(numbers) # [1, 3, 5]

GOOD: Use list comprehension (preferred)

GOOD: Use list comprehension (preferred)

numbers = [1, 2, 3, 4, 5, 6] numbers = [num for num in numbers if num % 2 != 0] print(numbers) # [1, 3, 5]
numbers = [1, 2, 3, 4, 5, 6] numbers = [num for num in numbers if num % 2 != 0] print(numbers) # [1, 3, 5]

GOOD: Use filter

GOOD: Use filter

numbers = [1, 2, 3, 4, 5, 6] numbers = list(filter(lambda x: x % 2 != 0, numbers)) print(numbers) # [1, 3, 5]
numbers = [1, 2, 3, 4, 5, 6] numbers = list(filter(lambda x: x % 2 != 0, numbers)) print(numbers) # [1, 3, 5]

GOOD: Iterate backwards (for in-place modification)

GOOD: Iterate backwards (for in-place modification)

numbers = [1, 2, 3, 4, 5, 6] for i in range(len(numbers) - 1, -1, -1): if numbers[i] % 2 == 0: del numbers[i] print(numbers) # [1, 3, 5]
undefined
numbers = [1, 2, 3, 4, 5, 6] for i in range(len(numbers) - 1, -1, -1): if numbers[i] % 2 == 0: del numbers[i] print(numbers) # [1, 3, 5]
undefined

3.
is
vs
==

3.
is
vs
==

The Problem

问题

python
undefined
python
undefined

Comparing values vs identity

Comparing values vs identity

a = [1, 2, 3] b = [1, 2, 3]
print(a == b) # True - same values print(a is b) # False - different objects
a = [1, 2, 3] b = [1, 2, 3]
print(a == b) # True - same values print(a is b) # False - different objects

Integer interning gotcha

Integer interning gotcha

x = 256 y = 256 print(x is y) # True (integers -5 to 256 are interned)
x = 257 y = 257 print(x is y) # False! (outside interning range)
undefined
x = 256 y = 256 print(x is y) # True (integers -5 to 256 are interned)
x = 257 y = 257 print(x is y) # False! (outside interning range)
undefined

The Rule

规则

  • Use
    ==
    to compare values
  • Use
    is
    only for identity (singletons like
    None
    ,
    True
    ,
    False
    )
python
undefined
  • 使用
    ==
    比较
  • 仅对身份标识(如
    None
    True
    False
    等单例对象)使用
    is
python
undefined

GOOD: Correct usage

GOOD: Correct usage

if value is None: ...
if value == other_value: ...
if value is None: ...
if value == other_value: ...

BAD: Don't use
is
for value comparison

BAD: Don't use
is
for value comparison

if value is 0: # Wrong! ...
undefined
if value is 0: # Wrong! ...
undefined

4. Variable Scope (LEGB)

4. Variable Scope (LEGB)

The Problem

问题

python
undefined
python
undefined

Closure gotcha

Closure gotcha

functions = [] for i in range(3): functions.append(lambda: i)
functions = [] for i in range(3): functions.append(lambda: i)

All return the same value!

All return the same value!

print([f() for f in functions]) # [2, 2, 2]
undefined
print([f() for f in functions]) # [2, 2, 2]
undefined

Why It Happens

原因

The lambda captures the variable
i
, not its value. By the time lambdas are called,
i
is 2.
Lambda表达式捕获的是变量
i
,而非其。当Lambda被调用时,
i
的值已经是2。

The Fixes

修复方案

python
undefined
python
undefined

GOOD: Capture value with default argument

GOOD: Capture value with default argument

functions = [] for i in range(3): functions.append(lambda i=i: i) # Default arg captures value print([f() for f in functions]) # [0, 1, 2]
functions = [] for i in range(3): functions.append(lambda i=i: i) # Default arg captures value print([f() for f in functions]) # [0, 1, 2]

GOOD: Use functools.partial

GOOD: Use functools.partial

from functools import partial
def return_value(x): return x
functions = [partial(return_value, i) for i in range(3)] print([f() for f in functions]) # [0, 1, 2]
undefined
from functools import partial
def return_value(x): return x
functions = [partial(return_value, i) for i in range(3)] print([f() for f in functions]) # [0, 1, 2]
undefined

UnboundLocalError

UnboundLocalError

python
undefined
python
undefined

BAD: This raises UnboundLocalError

BAD: This raises UnboundLocalError

x = 10
def increment(): x = x + 1 # Error! x is local but used before assignment return x
x = 10
def increment(): x = x + 1 # Error! x is local but used before assignment return x

GOOD: Use global (sparingly)

GOOD: Use global (sparingly)

def increment(): global x x = x + 1 return x
def increment(): global x x = x + 1 return x

BETTER: Avoid global, pass as parameter

BETTER: Avoid global, pass as parameter

def increment(x): return x + 1
undefined
def increment(x): return x + 1
undefined

5. String Concatenation

5. String Concatenation

Implicit Concatenation Gotcha

Implicit Concatenation Gotcha

python
undefined
python
undefined

Missing comma creates concatenation

Missing comma creates concatenation

items = [ "apple" "banana" # Oops! Missing comma "cherry" ] print(items) # ['applebanana', 'cherry']
items = [ "apple" "banana" # Oops! Missing comma "cherry" ] print(items) # ['applebanana', 'cherry']

CORRECT

CORRECT

items = [ "apple", "banana", "cherry", ]
undefined
items = [ "apple", "banana", "cherry", ]
undefined

Type Mixing

Type Mixing

python
undefined
python
undefined

BAD: Can't concatenate str and int

BAD: Can't concatenate str and int

name = "User" count = 42
name = "User" count = 42

message = "Hello " + name + ", you have " + count + " messages" # TypeError!

message = "Hello " + name + ", you have " + count + " messages" # TypeError!

GOOD: Use f-strings

GOOD: Use f-strings

message = f"Hello {name}, you have {count} messages"
message = f"Hello {name}, you have {count} messages"

GOOD: Use str()

GOOD: Use str()

message = "Hello " + name + ", you have " + str(count) + " messages"
undefined
message = "Hello " + name + ", you have " + str(count) + " messages"
undefined

6. Late Binding in Closures

6. Late Binding in Closures

The Problem

问题

python
undefined
python
undefined

Class method gotcha

Class method gotcha

class MyClass: def init(self, callbacks=[]): # BAD: Mutable default! self.callbacks = callbacks
def add_callback(self, func):
    self.callbacks.append(func)
obj1 = MyClass() obj2 = MyClass() obj1.add_callback(lambda: print("Hello"))
class MyClass: def init(self, callbacks=[]): # BAD: Mutable default! self.callbacks = callbacks
def add_callback(self, func):
    self.callbacks.append(func)
obj1 = MyClass() obj2 = MyClass() obj1.add_callback(lambda: print("Hello"))

obj2 also has the callback!

obj2 also has the callback!

print(len(obj2.callbacks)) # 1
undefined
print(len(obj2.callbacks)) # 1
undefined

The Fix

修复方案

python
class MyClass:
    def __init__(self, callbacks=None):
        self.callbacks = callbacks if callbacks is not None else []

    def add_callback(self, func):
        self.callbacks.append(func)
python
class MyClass:
    def __init__(self, callbacks=None):
        self.callbacks = callbacks if callbacks is not None else []

    def add_callback(self, func):
        self.callbacks.append(func)

7. Boolean Evaluation

7. Boolean Evaluation

Falsy Values

Falsy Values

python
undefined
python
undefined

These are all falsy

These are all falsy

falsy_values = [ False, None, 0, 0.0, 0j, "", [], {}, set(), range(0), ]
falsy_values = [ False, None, 0, 0.0, 0j, "", [], {}, set(), range(0), ]

Gotcha: Empty collections are falsy

Gotcha: Empty collections are falsy

data = [] if data: print("Has data") # Not printed else: print("No data") # Printed
data = [] if data: print("Has data") # Not printed else: print("No data") # Printed

But None and empty are different!

But None and empty are different!

if data is None: print("Is None") # Not printed elif data == []: print("Is empty list") # Printed
undefined
if data is None: print("Is None") # Not printed elif data == []: print("Is empty list") # Printed
undefined

Explicit Checks

Explicit Checks

python
undefined
python
undefined

BAD: Ambiguous check

BAD: Ambiguous check

def process(items): if not items: # Could be None OR empty return
def process(items): if not items: # Could be None OR empty return

GOOD: Be explicit about what you're checking

GOOD: Be explicit about what you're checking

def process(items): if items is None: raise ValueError("items cannot be None") if len(items) == 0: return # Early return for empty list
undefined
def process(items): if items is None: raise ValueError("items cannot be None") if len(items) == 0: return # Early return for empty list
undefined

8. Floating Point Precision

8. Floating Point Precision

The Problem

问题

python
undefined
python
undefined

Floating point arithmetic isn't exact

Floating point arithmetic isn't exact

print(0.1 + 0.2) # 0.30000000000000004 print(0.1 + 0.2 == 0.3) # False!
undefined
print(0.1 + 0.2) # 0.30000000000000004 print(0.1 + 0.2 == 0.3) # False!
undefined

The Fixes

修复方案

python
import math
from decimal import Decimal
python
import math
from decimal import Decimal

GOOD: Use math.isclose for comparisons

GOOD: Use math.isclose for comparisons

print(math.isclose(0.1 + 0.2, 0.3)) # True
print(math.isclose(0.1 + 0.2, 0.3)) # True

GOOD: Use Decimal for financial calculations

GOOD: Use Decimal for financial calculations

price = Decimal("19.99") tax = Decimal("0.0875") total = price * (1 + tax) print(total) # 21.739125
price = Decimal("19.99") tax = Decimal("0.0875") total = price * (1 + tax) print(total) # 21.739125

Round appropriately

Round appropriately

print(round(total, 2)) # 21.74
undefined
print(round(total, 2)) # 21.74
undefined

9. Exception Handling

9. Exception Handling

Bare Except

Bare Except

python
undefined
python
undefined

BAD: Catches everything, including KeyboardInterrupt

BAD: Catches everything, including KeyboardInterrupt

try: risky_operation() except: pass
try: risky_operation() except: pass

BAD: Too broad

BAD: Too broad

try: risky_operation() except Exception: pass # Silently ignores all errors
try: risky_operation() except Exception: pass # Silently ignores all errors

GOOD: Catch specific exceptions

GOOD: Catch specific exceptions

try: risky_operation() except ValueError as e: logger.error(f"Invalid value: {e}") except ConnectionError as e: logger.error(f"Connection failed: {e}") raise # Re-raise after logging
undefined
try: risky_operation() except ValueError as e: logger.error(f"Invalid value: {e}") except ConnectionError as e: logger.error(f"Connection failed: {e}") raise # Re-raise after logging
undefined

Exception Variable Scope

Exception Variable Scope

python
undefined
python
undefined

Python 3: Exception variable is deleted after except block

Python 3: Exception variable is deleted after except block

try: 1 / 0 except ZeroDivisionError as e: error = e # Save if needed print(e)
try: 1 / 0 except ZeroDivisionError as e: error = e # Save if needed print(e)

print(e) # NameError! e is deleted

print(e) # NameError! e is deleted

print(error) # OK
undefined
print(error) # OK
undefined

10. Dictionary Key Ordering

10. Dictionary Key Ordering

Modern Python (3.7+)

Modern Python (3.7+)

python
undefined
python
undefined

Since Python 3.7, dicts maintain insertion order

Since Python 3.7, dicts maintain insertion order

d = {"b": 2, "a": 1, "c": 3} print(list(d.keys())) # ['b', 'a', 'c'] - insertion order
d = {"b": 2, "a": 1, "c": 3} print(list(d.keys())) # ['b', 'a', 'c'] - insertion order

But comparison ignores order

But comparison ignores order

d1 = {"a": 1, "b": 2} d2 = {"b": 2, "a": 1} print(d1 == d2) # True
undefined
d1 = {"a": 1, "b": 2} d2 = {"b": 2, "a": 1} print(d1 == d2) # True
undefined

Gotcha: Changing Dict During Iteration

Gotcha: Changing Dict During Iteration

python
undefined
python
undefined

BAD: RuntimeError

BAD: RuntimeError

d = {"a": 1, "b": 2, "c": 3} for key in d: if d[key] == 2: del d[key] # RuntimeError: dictionary changed size during iteration
d = {"a": 1, "b": 2, "c": 3} for key in d: if d[key] == 2: del d[key] # RuntimeError: dictionary changed size during iteration

GOOD: Iterate over copy of keys

GOOD: Iterate over copy of keys

d = {"a": 1, "b": 2, "c": 3} for key in list(d.keys()): if d[key] == 2: del d[key] print(d) # {'a': 1, 'c': 3}
d = {"a": 1, "b": 2, "c": 3} for key in list(d.keys()): if d[key] == 2: del d[key] print(d) # {'a': 1, 'c': 3}

GOOD: Dict comprehension

GOOD: Dict comprehension

d = {"a": 1, "b": 2, "c": 3} d = {k: v for k, v in d.items() if v != 2}
undefined
d = {"a": 1, "b": 2, "c": 3} d = {k: v for k, v in d.items() if v != 2}
undefined

11. Import Gotchas

11. Import Gotchas

Circular Imports

Circular Imports

python
undefined
python
undefined

module_a.py

module_a.py

from module_b import func_b # Fails if module_b imports from module_a
def func_a(): return func_b()
from module_b import func_b # Fails if module_b imports from module_a
def func_a(): return func_b()

SOLUTION: Import inside function

SOLUTION: Import inside function

def func_a(): from module_b import func_b return func_b()
def func_a(): from module_b import func_b return func_b()

OR: Import module, not function

OR: Import module, not function

import module_b
def func_a(): return module_b.func_b()
undefined
import module_b
def func_a(): return module_b.func_b()
undefined

Module Name Shadowing

Module Name Shadowing

python
undefined
python
undefined

BAD: File named random.py shadows stdlib

BAD: File named random.py shadows stdlib

random.py (your file)

random.py (your file)

import random # Imports YOUR file, not stdlib! random.randint(1, 10) # AttributeError
import random # Imports YOUR file, not stdlib! random.randint(1, 10) # AttributeError

Python 3.13 now warns about this!

Python 3.13 now warns about this!

undefined
undefined

12. Class Attribute vs Instance Attribute

12. Class Attribute vs Instance Attribute

python
class MyClass:
    shared_list = []  # Class attribute - shared by all instances!

    def add_item(self, item):
        self.shared_list.append(item)

a = MyClass()
b = MyClass()
a.add_item("hello")
print(b.shared_list)  # ['hello'] - Oops!
python
class MyClass:
    shared_list = []  # Class attribute - shared by all instances!

    def add_item(self, item):
        self.shared_list.append(item)

a = MyClass()
b = MyClass()
a.add_item("hello")
print(b.shared_list)  # ['hello'] - Oops!

GOOD: Initialize in init

GOOD: Initialize in init

class MyClass: def init(self): self.items = [] # Instance attribute - unique to each instance
def add_item(self, item):
    self.items.append(item)
undefined
class MyClass: def init(self): self.items = [] # Instance attribute - unique to each instance
def add_item(self, item):
    self.items.append(item)
undefined

Quick Reference

快速参考

GotchaProblemSolution
Mutable defaults
def f(x=[])
Use
None
, create in function
Mutating while iteratingSkips itemsIterate over copy or use comprehension
is
vs
==
Identity vs equalityUse
is
only for
None
,
True
,
False
Late bindingCaptures variable, not valueUse default argument to capture
Falsy valuesEmpty != NoneBe explicit in checks
Float precision0.1 + 0.2 != 0.3Use
math.isclose()
or
Decimal
Bare exceptCatches too muchCatch specific exceptions
Dict iterationCan't modify duringIterate over
list(d.keys())
Circular importsImport errorsImport inside function or import module
Class attributesShared unexpectedlyInitialize in
__init__
陷阱类型问题解决方案
Mutable defaults
def f(x=[])
使用
None
,在函数内部创建
Mutating while iterating跳过元素迭代副本或使用推导式
is
vs
==
身份标识 vs 相等性仅对
None
True
False
使用
is
Late binding捕获变量而非值使用默认参数捕获值
Falsy values空值不等于None检查时明确判断
Float precision0.1 + 0.2 != 0.3使用
math.isclose()
Decimal
Bare except捕获范围过广捕获特定异常
Dict iteration迭代时无法修改迭代
list(d.keys())
Circular imports导入错误在函数内导入或导入整个模块
Class attributes意外共享属性
__init__
中初始化