Python(for循环进阶)

目录

1.range整数列表

1.基本用法回顾

特性:

惰性求值(懒加载)

[支持 len()、in、索引访问(r[i])](#支持 len()、in、索引访问(r[i]))

可以转换成列表,但通常不需要

range显示转换成list

1.需要修改元素

[2. 需要切片赋值](#2. 需要切片赋值)

3.传给只接受列表的函数

[2.扩展:使用 range 实现自定义间隔的循环](#2.扩展:使用 range 实现自定义间隔的循环)

[3.扩展:range 与 len 的替代方案](#3.扩展:range 与 len 的替代方案)

2.累加

1.累加基础回顾

2.扩展:加权求和

[zip() 的作用](#zip() 的作用)

3.扩展:累乘与对数累加

[使用 round() 四舍五入](#使用 round() 四舍五入)

[4.扩展:使用内置函数 sum()、math.fsum() 提高精度](#4.扩展:使用内置函数 sum()、math.fsum() 提高精度)

[5.扩展:累加时进行变换(map 替代)](#5.扩展:累加时进行变换(map 替代))

3.计数器

1.基础计数器回顾

[2.扩展:使用 collections.Counter](#2.扩展:使用 collections.Counter)

3.扩展:多条件计数与分组

[4. 扩展:使用 defaultdict(int) 实现更灵活的计数](#4. 扩展:使用 defaultdict(int) 实现更灵活的计数)

[defaultdict(int) 的含义](#defaultdict(int) 的含义)

[4.if 判断在 for 循环中的高级应用](#4.if 判断在 for 循环中的高级应用)

[1.any() 与 all() 替代显式循环](#1.any() 与 all() 替代显式循环)

any(iterable)

all(iterable)

2.在循环中捕获异常

核心机制:try-except

[3.使用 for-else 实现"查找-未找到"模式](#3.使用 for-else 实现“查找-未找到”模式)

[4.使用 filter() 替代带 if 的循环](#4.使用 filter() 替代带 if 的循环)

lambda表达式

[代码中的 lambda](#代码中的 lambda)

[lambda 表达式的特点](#lambda 表达式的特点)

5.循环的进阶工具与模式

[1.使用 enumerate 获得索引的扩展](#1.使用 enumerate 获得索引的扩展)

[2. 使用 zip 并行遍历的扩展](#2. 使用 zip 并行遍历的扩展)

3.无限序列生成:itertools.count、cycle

[4.使用 accumulate 进行累加](#4.使用 accumulate 进行累加)

[5. 使用 for 循环实现滑动窗口](#5. 使用 for 循环实现滑动窗口)

6.性能对比与优化建议

[1.列表推导式 vs for循环追加](#1.列表推导式 vs for循环追加)

[2.使用 map 替代显式循环](#2.使用 map 替代显式循环)

[3. 尽量减少循环内的属性查找](#3. 尽量减少循环内的属性查找)

[4. 使用 timeit 测量不同累加方式](#4. 使用 timeit 测量不同累加方式)


1.range整数列表

range 是 Python 内置的不可变序列类型,专门用于生成等间隔的整数序列。

1.基本用法回顾

复制代码
# 1. 直接传数字
for i in range(5):          # 等价于 0,1,2,3,4
    print(i)

# 2. 先定义变量再传入
stop = 5
for i in range(stop):       # 这时 stop 已定义,正常运行
    print(i)                #0,1,2,3,4

# 3. 带起点的 range(start, stop)
for i in range(2, 6):       # 2,3,4,5
    print(i)

# 4. 带步长的 range(start, stop, step)
for i in range(1, 10, 2):   # 1,3,5,7,9
    print(i)

# 5. 负步长(倒序)
for i in range(5, 0, -1):   # 5,4,3,2,1
    print(i)

特性:

惰性求值(懒加载)

不预先存储所有数字,只记下起点、终点、步长,每次循环时才"现算"一个数。

复制代码
r = range(1, 1000000)   # 想表示 1 到 999999
  • 如果是列表,内存里要立刻存下 100 万个整数,很占地方。

  • range 只记住了 start=1, stop=1000000, step=1,占用空间极小。

当你 for i in r: 的时候,它才根据步长逐个 算出下一个数字。

这就叫"惰性"------不着急把所有数据都准备好,用到时再生成。

支持 len()in、索引访问(r[i]

虽然 range 没把数字全存起来,但你可以像使用列表一样,用这些操作

复制代码
r = range(10, 20, 2)   # 表示 10, 12, 14, 16, 18

print(len(r))       # 5   → 直接根据公式算出来
print(14 in r)      # True → 不是挨个遍历,是通过数学计算判断
print(r[2])         # 14   → 直接计算 start + 2 * step = 10 + 4 = 14

这些操作都是 O(1) 的快速计算,不需要把数字全展开,非常高效。

可以转换成列表,但通常不需要

确实能把 range 变成一个真实的列表。

但如果你只是为了循环,直接用 range 就够了 ,没必要额外转成列表去占用内存。

只有在需要修改元素、需要切片赋值、或者要传给某个只接受列表的函数时,才考虑转换。

range显示转换成list

当需要以下这些操作时,就必须把 range 显式转换成 list,否则会报错或得不到想要的结果:

1.需要修改元素

range 是不可变序列,不能像列表那样给某个位置赋新值。

复制代码
#  错误:range 不支持元素赋值
r = range(5)
r[2] = 10      # TypeError: 'range' object does not support item assignment

#  正确:先转成列表再修改
lst = list(range(5))
lst[2] = 10    # lst 现在是 [0, 1, 10, 3, 4]

报错

Traceback (most recent call last):

File "c:\Users\wozha\Desktop\vs\python\1.py", line 3, in <module>

r[2] = 10 # TypeError: 'range' object does not support item assignment

TypeError: 'range' object does not support item assignment

2. 需要切片赋值

列表可以一次性替换掉一段连续的元素,range 做不到。

复制代码
#  错误:range 不支持切片赋值
r = range(10)
r[2:5] = [20, 30]   # TypeError

#  正确:转为列表后切片赋值
lst = list(range(10))   # [0,1,2,3,4,5,6,7,8,9]
lst[2:5] = [20, 30]     # 用两个元素替换掉索引 2,3,4 的三个位置
print(lst)              # [0, 1, 20, 30, 5, 6, 7, 8, 9]

3.传给只接受列表的函数

有些函数或方法要求传入 list 类型 ,传 range 会报错。典型如:

  • random.shuffle():需要原地打乱顺序,必须传可变列表。

  • 一些只声明了 list 参数类型的旧接口或第三方库函数。

    import random

    错误:shuffle 不接受 range

    r = range(10)
    random.shuffle(r) # TypeError: 'range' object does not support item assignment

    正确:先转成列表

    numbers = list(range(10))
    random.shuffle(numbers)
    print(numbers) # 例如 [3, 7, 0, 9, 1, 5, 8, 2, 4, 6]

range 是不可变序列 ,而 random.shuffle 要求原地修改序列,两者冲突

random.shuffle(x)直接打乱传入的序列 x (就地修改),它内部通过随机交换两个位置的元素来实现洗牌。

2.扩展:使用 range 实现自定义间隔的循环

复制代码
# 反向步长
for i in range(10, 0, -2):
    print(i)   # 10, 8, 6, 4, 2

# 生成等差数列,但需要浮点步长时怎么办?range 不接受浮点,可以用 numpy.arange 或手动 while
# 或者使用 itertools.count 配合 takewhile

3.扩展:rangelen 的替代方案

当需要遍历列表索引时,enumeraterange(len(...)) 更优雅:

len() 只是计算了列表的长度,返回一个数字。比如 len(fruits) 返回 3。这个 3 被用在 range() 里,生成了一个索引序列 0, 1, 2。然后 for 循环依次取这些索引,通过 fruits[i] 获取元素,再用 print() 输出。

enumerate 是 Python 内置函数,用于将一个可迭代对象 (如列表、元组、字符串)组合成一个索引序列 ,让你在循环中同时获取元素的索引元素本身

复制代码
fruits = ["苹果", "香蕉", "橙子"]
# 不推荐
for i in range(len(fruits)):
    print(f"{i}: {fruits[i]}")

# 推荐
for i, fruit in enumerate(fruits):
    print(f"{i}: {fruit}")

# 如果还需要修改原列表元素,仍然可以用 range,但可以结合 enumerate 和索引赋值
for i, fruit in enumerate(fruits):
    fruits[i] = fruit + "好吃"
print("修改后:", fruits)   #修改后: ['苹果好吃', '香蕉好吃', '橙子好吃']

2.累加

1.累加基础回顾

复制代码
total = 0
for i in range(1, 101):
    total += i
print(total)

2.扩展:加权求和

复制代码
scores = [85, 92, 78, 88]      # 成绩
weights = [0.3, 0.2, 0.4, 0.1] # 对应的权重
weighted_sum = 0                # 初始化加权和

for s, w in zip(scores, weights):
    weighted_sum += s * w       # 累加成绩×权重

print(weighted_sum)             # 输出 83.9

zip() 的作用

  • zip(scores, weights) 将两个列表中位置相同的元素打包成元组:

    • 第1次迭代:(85, 0.3)

    • 第2次迭代:(92, 0.2)

    • 第3次迭代:(78, 0.4)

    • 第4次迭代:(88, 0.1)

  • 通过 for s, w in zip(...) 将每个元组解包给变量 sw,实现并行遍历。

3.扩展:累乘与对数累加

复制代码
import math          # 导入了 math 模块,但本代码中并未使用(可以删除)
data = [2, 4, 8]     # 原始数据
product = 1          # 初始化乘积为 1(累乘的起点)
for x in data:       # 遍历列表中的每个数
    product *= x     # 累乘:product = product * x
geometric_mean = product ** (1 / len(data))   # 几何平均 = 所有数的乘积 开 n 次方
print(geometric_mean)   # 输出 3.9999999999999996 

** 是 Python 中的幂运算符,用于计算一个数的乘方(指数运算)。

使用 round() 四舍五入
复制代码
geometric_mean = round(product ** (1/len(data)), 10)  # 保留10位小数

4.扩展:使用内置函数 sum()math.fsum() 提高精度

sum() 可以对任何可迭代对象求和,底层使用 C 循环,比手动 for 快。

复制代码
total = sum(range(1, 101))   # 5050
print(total)

对于浮点数累加,math.fsum() 能提供更高精度(使用 Kahan 求和算法)。

复制代码
import math
float_list = [0.1] * 10
print(sum(float_list))       # 0.9999999999999999
print(math.fsum(float_list)) # 1.0

5.扩展:累加时进行变换(map 替代)

如果需要先对元素进行某种变换再累加,可以用生成器表达式:

复制代码
# 平方和
numbers = [1,2,3,4]
total = sum(x**2 for x in numbers)   # 30

这比手动for更简洁高效

3.计数器

统计与分组

1.基础计数器回顾

复制代码
count = 0
data = [2, 4, 8] 
condition = 2
for x in data:
    if x==condition:
        count += 1
print(count)

2.扩展:使用 collections.Counter

当需要统计多个类别时,手动计数器写起来较繁琐。Counter 是专门为计数设计的字典子类:

复制代码
from collections import Counter
data = ['a', 'b', 'a', 'c', 'b', 'a']
counter = Counter(data)
print(counter)   # Counter({'a':3, 'b':2, 'c':1})

甚至可以一次性统计频率

复制代码
from collections import Counter
sentence = "hello world"
counter = Counter(sentence)
print(counter)   # 包含空格、字母的计数
                 # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})

3.扩展:多条件计数与分组

假设有一个学生列表(姓名、成绩),统计及格与不及格人数:

复制代码
students = [('Alice', 85), ('Bob', 58), ('Charlie', 92), ('David', 45)]
pass_cnt = 0
fail_cnt = 0
for name, score in students:
    if score >= 60:
        pass_cnt += 1
    else:
        fail_cnt += 1

Counter 也可以,但需要先映射:

复制代码
status = ['及格' if score>=60 else '不及格' for _, score in students]
counter = Counter(status)
print(counter)   # Counter({'及格':2, '不及格':2})

循环部分:for _, score in students

  • students 是一个可迭代对象,通常包含多个学生的信息。假设每个元素是一个元组或列表,例如 [('张三', 85), ('李四', 42), ('王五', 73), ('赵六', 58)]

  • _ 是一个常见的占位符变量名,表示忽略该位置的值。这里假设每个学生数据的第一项(如姓名)不需要使用,只关心第二项(分数)。

  • score 接收每个学生的分数。

4. 扩展:使用 defaultdict(int) 实现更灵活的计数

复制代码
from collections import defaultdict
counts = defaultdict(int)
for x in data:
    counts[x] += 1

defaultdict(int) 的含义

  • defaultdict 是内置字典 dict 的子类,当访问的键不存在时,不会抛出 KeyError,而是自动调用传入的工厂函数生成一个默认值。

  • int 作为工厂函数,调用 int() 返回整数 0。所以 counts[x] 如果不存在,会自动初始化为 0,然后 += 1 将值变为 1

  • 如果键已存在,counts[x] 返回当前值,然后加 1。


4.if 判断在 for 循环中的高级应用

1.any()all() 替代显式循环

当只需要判断是否存在或是否全部满足时,可以用内置函数:

any(iterable)

  • 含义 :只要可迭代对象中至少有一个元素为 True ,就返回 True;如果所有元素都为 False(或可迭代对象为空),则返回 False

  • 逻辑 :相当于" "运算的短路版本------一旦遇到第一个真值,立即返回 True

all(iterable)

  • 含义 :只有可迭代对象中所有元素都为 True ,才返回 True;如果存在至少一个 False,则返回 False(空的可迭代对象返回 True)。

  • 逻辑 :相当于" "运算的短路版本------一旦遇到第一个假值,立即返回 False

复制代码
numbers = [2,4,6,8]
# 是否存在奇数?
has_odd = any(x % 2 == 1 for x in numbers)  
print(has_odd)     # False
# 是否全部为偶数?
all_even = all(x % 2 == 0 for x in numbers)  
print(all_even)    # True

这些函数内部会短路(类似 break),比手动 for 更简洁。

2.在循环中捕获异常

当遍历可能引发异常的操作时,可以用 try-except 包裹:

复制代码
data = ['1', '2', 'abc', '4']
total = 0
for item in data:
    try:
        total += int(item)
    except ValueError:
        print(f"跳过非数字: {item}")
print(total)   # 1+2+4=7

核心机制:try-except

  • int(item) 如果 item 不是合法的整数表示(如 'abc'),会抛出 ValueError 异常。

  • except ValueError 捕获该异常,执行异常处理代码(打印提示),然后继续下一次循环。

  • 正常转换的项('1', '2', '4')会被成功累加到 total

3.使用 for-else 实现"查找-未找到"模式

复制代码
# 查找列表中第一个大于 50 的数
numbers = [12, 45, 67, 23]
for n in numbers:
    if n > 50:
        print(f"找到: {n}")
        break
else:
    print("未找到大于50的数")

else 在未触发 break 时执行,非常适合这种"如果找到了就处理,没找到就做另一件事"的场景。

4.使用 filter() 替代带 if 的循环

复制代码
# 筛选出偶数
numbers = range(10)
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
# 等价于列表推导式 [x for x in numbers if x % 2 == 0]

filter 返回迭代器,惰性求值。

filter 是 Python 内置函数,用于筛选可迭代对象中满足条件的元素,返回一个迭代器。

lambda表达式

代码中的 lambda

复制代码
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
  • lambda x: x % 2 == 0 定义了一个匿名函数,等价于:

    复制代码
    def is_even(x):
        return x % 2 == 0
  • filter 函数接收两个参数:一个函数和一个可迭代对象。它会将 numbers 中的每个元素传入 lambda 函数,保留返回 True 的元素。

lambda 表达式的特点

  • 单行表达式,不能包含语句或赋值。

  • 常用于需要简单函数但不想正式定义 def 的场合(如 filter, map, sortedkey 参数等)。

5.循环的进阶工具与模式

1.使用 enumerate 获得索引的扩展

enumerate 可以指定起始值:

复制代码
fruits = ["苹果", "香蕉", "橙子"]
for idx, fruit in enumerate(fruits, start=1):
    print(f"{idx}. {fruit}")

2. 使用 zip 并行遍历的扩展

zip 可以处理不等长序列(以最短的为准)。若要以最长的为准并填充缺失值,使用 itertools.zip_longest

复制代码
from itertools import zip_longest
a = [1,2,3]
b = ['a','b']
for x, y in zip_longest(a, b, fillvalue=None):
    print(x, y)   # 1 a, 2 b, 3 None

3.无限序列生成:itertools.countcycle

count(start, step) 生成无限递增序列,常与 takewhile 配合:

复制代码
from itertools import count, islice
for i in count(10, 2):
    if i > 20:
        break
    print(i)   # 10,12,14,16,18,20

# 或者使用 islice 限制个数
for i in islice(count(0, 5), 3):  # 0,5,10
    print(i)

cycle 重复遍历一个序列:

复制代码
from itertools import cycle
colors = ['红', '绿', '蓝']
for i, color in zip(range(5), cycle(colors)):
    print(i, color)   # 0红,1绿,2蓝,3红,4绿

4.使用 accumulate 进行累加

itertools.accumulate 返回累积和(或其他二元运算):

复制代码
from itertools import accumulate
data = [1,2,3,4]
cumsum = list(accumulate(data))   # [1,3,6,10]
# 指定运算符
import operator
cumprod = list(accumulate(data, operator.mul))  # [1,2,6,24]

5. 使用 for 循环实现滑动窗口

复制代码
def sliding_window(iterable, size):
    result = []
    for i in range(len(iterable) - size + 1):
        result.append(iterable[i:i+size])
    return result

print(sliding_window([1,2,3,4,5], 3))  # [[1,2,3],[2,3,4],[3,4,5]]

6.性能对比与优化建议

1.列表推导式 vs for循环追加

复制代码
import timeit
# 使用 for 循环 + append
def loop_append():
    result = []
    for i in range(1000):
        result.append(i**2)
    return result

# 使用列表推导式
def list_comp():
    return [i**2 for i in range(1000)]

print(timeit.timeit(loop_append, number=10000))
print(timeit.timeit(list_comp, number=10000))
# 列表推导式通常快 20%~50%

2.使用 map 替代显式循环

当只需要对每个元素应用函数时,map 通常比 for 循环更快(因为内部 C 循环):

复制代码
result = list(map(lambda x: x**2, range(1000)))

3. 尽量减少循环内的属性查找

复制代码
data = [1,2,3,4]
# 不推荐
for i in range(len(data)):
    data[i] = data[i] * 2
print(data)
# 推荐:将 data 赋值给局部变量
lst = data
for i in range(len(lst)):
    lst[i] = lst[i] * 2
print(data)
# 或者使用 enumerate
for i, val in enumerate(data):
    data[i] = val * 2
print(data)

4. 使用 timeit 测量不同累加方式

复制代码
# 手动循环
def manual_sum(n):
    s = 0
    for i in range(n):
        s += i
    return s

# 内置 sum
def builtin_sum(n):
    return sum(range(n))

print(timeit.timeit(lambda: manual_sum(10000), number=1000))
print(timeit.timeit(lambda: builtin_sum(10000), number=1000))
# sum 通常更快

感谢你的观看,期待我们下次再见!

相关推荐
AI玫瑰助手1 小时前
Python运算符:算术运算符(加减乘除取模幂)详解
开发语言·python
xiaoye-duck1 小时前
Qt 信号与槽深度解析:connect 用法、自定义信号槽与 Lambda 实战
开发语言·qt
情绪总是阴雨天~1 小时前
深度解析:LangChain、Agent、RAG、FC、ReAct、LangGraph、A2A、MCP — 区别、联系与全景图
python·langchain·agent·rag·langgraph·mcp·a2a
lsx2024062 小时前
C AI 编程助手:助力开发者高效编程
开发语言
沐知全栈开发2 小时前
Eclipse 编译项目指南
开发语言
小李不困还能学2 小时前
PyCharm2025.2下载安装配置保姆级教程
python·pycharm
无限进步_2 小时前
C++11概览与统一初始化
开发语言·c++
笨蛋不要掉眼泪2 小时前
Java并发编程:内存可见性与synchronized同步机制
java·开发语言·并发
A000—ic测试座(陈佳鑫)2 小时前
芯片高温加速寿命测试:除了初始测试还有哪些关键条件?
网络·人工智能·python