凌晨三点。屏幕前的你盯着那段while循环,心里一万头草泥马奔腾——又死循环了!
这事儿我太懂了。刚工作那年,写了个爬虫脚本,while True里忘了加break。结果?服务器跑了一晚上,产生了3.2GB的垃圾日志,第二天被运维老哥骂得狗血淋头。
说实话,循环是编程里最容易写但最难写好的东西。for、while看起来简单对吧?但你知道循环的else子句能干啥吗?知道什么时候该用break而不是标志变量吗?据Stack Overflow统计,35%的Python性能问题都源于低效的循环写法。
今天咱们就把循环这玩意儿扒个底朝天。不仅要教你怎么写,更要教你——怎么写得让三个月后的自己不想骂娘。
大多数人写循环,就跟开车只会油门刹车一样——能跑,但不优雅。我总结了三大硬伤:
1. for和while分不清场景 看过太多代码,该用for的地方写while,搞个计数器i自己加。累不累?
2. break/continue用得稀里糊涂 有的人压根不用,全靠if嵌套;有的人滥用,逻辑跳来跳去跟迷宫似的。
3. 循环else?那是啥? 十个Python开发,九个不知道for...else的存在。这个特性能让代码简洁30%,但就是没人用!
python# 这段代码我在Code Review时见过不少
i = 0
while i < len(data):
item = data[i]
if item > 100:
print(item)
i += 1 # 忘了这行?恭喜你喜提死循环
还有更离谱的:
python# 某同事写的"查找用户"逻辑
found = False
for user in users:
if user.id == target_id:
found = True
target_user = user
break
if found:
process(target_user)
else:
print("用户不存在")
看着头疼吧?其实循环else一行就搞定。待会儿我教你。
很多人以为for循环是"遍历列表"。错!Python的for本质是:迭代可迭代对象。
python# 这两段代码等价
for item in [1, 2, 3]:
print(item)
# 底层实际上是这样
iterator = iter([1, 2, 3])
while True:
try:
item = next(iterator)
print(item)
except StopIteration:
break

理解这点很关键。为啥?因为你能自己造迭代器!
pythonclass Countdown:
"""自定义倒计时迭代器"""
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
# 直接用for循环
for num in Countdown(5):
print(num)

我在做日志分析系统时,就自己写了个迭代器来逐行读取100GB的日志文件——内存占用始终不超过50MB。传统的readlines()早爆了。
while就一个活儿:只要条件为真,就不停转。听起来简单,但陷阱多。
python# ❌ 经典反面教材
while True:
data = fetch_data()
if not data:
break # 啥时候跳出?谁知道呢
process(data)
# ✅ 显式条件更清晰
data = fetch_data()
while data:
process(data)
data = fetch_data()
我的原则:能用for就别用while。除非你真的需要"条件驱动"而不是"序列遍历"。
先看个真实场景:从列表里找质数。
pythondef find_primes(numbers):
"""找出所有质数"""
primes = []
for num in numbers:
if num < 2:
continue # 跳过小于2的数
# 检查是否为质数
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
break # 发现因数,不是质数
else:
# 🔥 重点:只有内循环正常结束(没遇到break)才执行
primes.append(num)
return primes
# 测试
print(find_primes([2, 3, 4, 5, 6, 7, 8, 9, 10]))

else子句的规则:
这招能省掉90%的标志变量!再看个例子:
python# 用户登录验证
def authenticate(username, password, user_list):
for user in user_list:
if user.name == username:
if user.check_password(password):
return user
else:
return None # 密码错误
else:
# 循环结束都没找到用户
return None
我们团队自从推广这个技巧,代码review时间平均减少了25%。
break:直接滚蛋,跳出整个循环。
continue:跳过本次,继续下一轮。
听起来简单?来看个爬虫场景:
pythondef crawl_products(urls, max_success=10):
"""
爬取商品数据,成功10个就停
:param urls: 待爬取的URL列表
:param max_success: 最大成功数
"""
success_count = 0
results = []
for url in urls:
# 跳过无效URL
if not url.startswith("http"):
print(f"跳过无效URL: {url}")
continue # 不影响后续循环
try:
data = fetch_data(url)
# 数据校验
if not data or len(data) < 100:
print(f"数据异常: {url}")
continue # 重点:遇到脏数据直接跳过
results.append(data)
success_count += 1
# 达到目标数量就撤
if success_count >= max_success:
print("已达目标,停止爬取")
break # 立即终止循环
except Exception as e:
print(f"爬取失败 {url}: {e}")
continue # 异常也不影响后续
return results
我踩过的坑:
别再写for i in range(len(list))了!太丑。
python# ❌ 老土写法
fruits = ["苹果", "香蕉", "橙子"]
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
# ✅ Pythonic写法
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 🔥 高级用法:自定义起始索引
for index, fruit in enumerate(fruits, start=1):
print(f"第{index}个: {fruit}")

zip的威力 —— 同时遍历多个列表:
pythonnames = ["张三", "李四", "王五"]
ages = [25, 30, 28]
cities = ["北京", "上海", "深圳"]
# 传统写法(恶心)
for i in range(len(names)):
print(f"{names[i]}, {ages[i]}岁, 来自{cities[i]}")
# zip写法(优雅)
for name, age, city in zip(names, ages, cities):
print(f"{name}, {age}岁, 来自{city}")

实战案例:我做过一个Excel数据对比工具,需要逐行比较两个表格。用zip后,代码从80行缩减到35行,执行效率提升40%。
当循环只是为了生成列表,推导式能让代码短到爆。
python# 场景:过滤奇数并平方
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 传统for循环
result = []
for num in numbers:
if num % 2 == 1:
result.append(num ** 2)
# 列表推导式(一行搞定)
result = [num ** 2 for num in numbers if num % 2 == 1]
性能实测(100万次循环):
但是!别滥用。这种就过分了:
python# ❌ 过度复杂,可读性为负
result = [x**2 if x > 5 else x**3 for x in range(20) if x % 2 == 0 if x != 10]
我的团队规范:推导式超过一行就拆成普通循环。
场景:处理5GB的CSV日志文件,提取错误记录。
pythondef process_large_log(file_path, error_keyword="ERROR"):
"""
逐行处理大文件,避免内存爆炸
:param file_path: 日志文件路径
:param error_keyword: 错误关键词
:return: 错误行数统计
"""
error_count = 0
error_details = []
with open(file_path, 'r', encoding='utf-8') as file:
for line_num, line in enumerate(file, start=1):
# 空行跳过
if not line.strip():
continue
# 只处理错误行
if error_keyword in line:
error_count += 1
error_details.append({
'line': line_num,
'content': line.strip()
})
# 内存保护:只保留最近1000条
if len(error_details) > 1000:
error_details.pop(0)
# 进度提示(每10万行输出一次)
if line_num % 100000 == 0:
print(f"已处理 {line_num} 行,发现 {error_count} 个错误")
return error_count, error_details
# 实际使用
count, details = process_large_log("app.log")
print(f"总错误数:{count}")
关键点:
enumerate(file, start=1) → 直接获取行号continue跳过空行 → 提速20%真实效果:
readlines():快8倍,内存省400倍场景:调用第三方API,网络不稳定需要重试。
pythonimport time
def fetch_with_retry(url, max_retries=3, backoff=2):
"""
带指数退避的重试机制
:param url: 请求URL
:param max_retries: 最大重试次数
:param backoff: 退避基数(秒)
:return: 响应数据或None
"""
attempt = 0
while attempt < max_retries:
try:
print(f"第{attempt + 1}次尝试...")
response = requests.get(url, timeout=5)
# 成功就立即返回
if response.status_code == 200:
return response.json()
# 4xx错误不重试(客户端问题)
if 400 <= response.status_code < 500:
print(f"客户端错误 {response.status_code},停止重试")
break
except requests.Timeout:
print("请求超时")
except requests.RequestException as e:
print(f"请求异常: {e}")
attempt += 1
# 指数退避:2秒、4秒、8秒...
if attempt < max_retries:
wait_time = backoff ** attempt
print(f"等待 {wait_time} 秒后重试...")
time.sleep(wait_time)
else:
# 🔥 循环正常结束(达到max_retries)执行这里
print("达到最大重试次数,放弃请求")
return None
# 使用示例
data = fetch_with_retry(url)
if data:
process(data)
亮点设计:
场景:数据库查询10万条记录,一次性加载会OOM。
pythondef batch_process_users(batch_size=1000):
"""
分批处理用户数据
:param batch_size: 每批数量
"""
offset = 0
total_processed = 0
while True:
# 分批查询
users = db.query(User).limit(batch_size).offset(offset).all()
# 没数据了就退出
if not users:
print("所有数据处理完成")
break
# 处理本批数据
for user in users:
try:
# 业务逻辑:发送邮件
send_email(user.email, "促销通知")
total_processed += 1
except Exception as e:
print(f"处理用户 {user.id} 失败: {e}")
continue # 一个失败不影响其他
offset += batch_size
print(f"已处理 {total_processed} 个用户")
# 防止过载:每批间隔0.5秒
time.sleep(0.5)
return total_processed
# 执行
total = batch_process_users(batch_size=500)
print(f"共成功处理 {total} 个用户")
核心技巧:
"能用for就别用while,能用推导式就别用for" —— 选对工具,事半功倍。
"循环else不是多余,是标志变量的掘墓人" —— 用过都说好。
"break是决断,continue是跳过,没有中间状态" —— 别犹豫,该断就断。
pythondef retry_template(func, max_attempts=3):
"""通用重试装饰器"""
attempt = 0
while attempt < max_attempts:
try:
return func()
except Exception as e:
attempt += 1
if attempt >= max_attempts:
raise
time.sleep(2 ** attempt)
pythondef safe_infinite_loop(condition_func, action_func, interval=1):
"""带紧急出口的无限循环"""
iteration = 0
max_iterations = 10000 # 防护阀
while condition_func():
action_func()
iteration += 1
if iteration >= max_iterations:
raise RuntimeError("循环次数超限,可能存在逻辑错误")
time.sleep(interval)
循环进阶 ↓ 生成器与yield(内存优化终极方案) ↓ 迭代器协议深入(自定义迭代行为) ↓ asyncio异步循环(高并发场景)
话题1:你写过最离谱的死循环是啥样的?怎么发现的?
话题2:有人用过循环else吗?在什么场景下觉得特别好用?
实战挑战:用今天学的技巧优化这段代码:
python# 找出列表中第一个大于100的数,没找到输出"未找到"
numbers = [23, 45, 67, 120, 89, 150]
found = False
result = None
for num in numbers:
if num > 100:
found = True
result = num
break
if found:
print(f"找到了:{result}")
else:
print("未找到")
评论区留下你的答案,看谁写得最Pythonic!点赞最高的我送一本《流畅的Python》。
标签推荐:#Python循环 #编程技巧 #代码优化 #性能调优 #Python进阶
如果这篇文章让你对循环有了新认识,转发给还在写for i in range(len(list))的朋友吧。记住——好的循环不是跑得快,而是让人一眼就看懂在干嘛。
咱们下期聊聊生成器和yield,那才是真正的内存杀手锏!👋
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!