Python基础range()函数的参数详解与高效用法一、开篇for循环的灵魂伴侣在Python中range()函数几乎和for循环形影不离。每当你需要重复N次或遍历一段数字时就会用到它foriinrange(10):print(i)# 0到9foriinrange(5,10):print(i)# 5到9foriinrange(0,10,2):print(i)# 0, 2, 4, 6, 8看起来简单但range()有一些不太广为人知的细节它是惰性计算的、它支持负数步长、它可以用in做O(1)的成员检查、它甚至可以和len()配合使用。⌨️ 更重要的很多Python初学者滥用range(len(...))而错过了更Pythonic的迭代方式。今天就来彻底掌握range()。 全文将围绕三个核心问题展开range()的参数到底怎么用、它为什么高效、什么时候该用什么时候不该用。二、range()的三种参数形式2.1 单参数range(stop)# range(stop) —— 从0到stop-1# 等价于数学中的 [0, stop)foriinrange(5):print(i,end )# 输出: 0 1 2 3 4# 转换为列表查看内容print(list(range(5)))# [0, 1, 2, 3, 4]print(list(range(1)))# [0]print(list(range(0)))# [] —— stop0 生成空range# print(list(range(-5))) # [] —— 负数也一样是空的# 记忆技巧range(n) 产生 n 个数字从0到n-12.2 双参数range(start, stop)# range(start, stop) —— 从start到stop-1# 等价于数学中的 [start, stop)foriinrange(5,10):print(i,end )# 输出: 5 6 7 8 9print(list(range(3,8)))# [3, 4, 5, 6, 7]print(list(range(-3,3)))# [-3, -2, -1, 0, 1, 2]print(list(range(10,5)))# [] —— start stop空range# 计算range中的元素个数stop - start当start stop时print(len(range(5,10)))# 5print(len(range(-3,3)))# 62.3 三参数range(start, stop, step)# range(start, stop, step) —— 从start开始每次加step直到达到或超过stop# 正步长print(list(range(0,10,2)))# [0, 2, 4, 6, 8] —— 偶数print(list(range(1,10,2)))# [1, 3, 5, 7, 9] —— 奇数print(list(range(0,100,10)))# [0, 10, 20, 30, ..., 90]# 负步长倒序print(list(range(10,0,-1)))# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]print(list(range(10,0,-2)))# [10, 8, 6, 4, 2]print(list(range(5,-5,-1)))# [5, 4, 3, 2, 1, 0, -1, -2, -3, -4]# ⚠️ step不能为0# list(range(0, 10, 0)) # ValueError: range() arg 3 must not be zero# 步长的符号决定方向# step 0: 从start向stop增长要求 start stop 才有元素# step 0: 从start向stop减小要求 start stop 才有元素print(list(range(0,10,2)))# 有元素: start(0) stop(10), step(2) 0print(list(range(10,0,2)))# 空start(10) stop(0) 但 step(2) 0print(list(range(10,0,-2)))# 有元素: start(10) stop(0), step(-2) 02.4 参数名称# range的参数也可以使用关键字r1range(stop5)r2range(start2,stop8)r3range(start10,stop0,step-2)# 但不常用的组合可能无法使用关键字# range(start0, 10) # SyntaxError位置参数不能在关键字参数后面# 建议大多数情况下使用位置参数更简洁# range(5) 而不是 range(stop5)# range(2, 8) 而不是 range(start2, stop8)三、range对象的高效特性3.1 惰性计算Lazy Evaluation# range() 不创建包含所有元素的列表# 它是一个序列对象按需生成值importsys# range对象非常小rrange(1000000)# 一百万个元素lstlist(range(1000000))# 一百万个元素的列表print(frange对象大小:{sys.getsizeof(r):,}bytes)# 约48 bytesprint(f列表对象大小:{sys.getsizeof(lst):,}bytes)# 约8,000,000 bytes# range只在迭代时计算下一个值不存储所有值# 这是它比列表高效得多的原因# range对象的重要特性rrange(10)print(len(r))# 10 —— O(1)计算print(r[0])# 0 —— 通过公式计算print(r[5])# 5print(r[-1])# 9 —— 支持负索引# print(r[100]) # IndexError3.2 O(1)的成员检查# range的 in 操作是O(1)的# 它通过数学公式检查而不是遍历所有元素rrange(0,1000000,2)print(500000inr)# TrueO(1)时间print(500001inr)# FalseO(1)时间# 对比列表的O(n)检查importtimeit lstlist(r)t_rangetimeit.timeit(lambda:999998inr,number10000)t_listtimeit.timeit(lambda:999998inlst,number10000)print(frange in:{t_range:.4f}s)# 极快print(flist in:{t_list:.4f}s)# 慢得多# range的in检查原理defexplain_range_contains(r,x):解释range如何做成员检查start,stop,stepr.start,r.stop,r.stepifstep0:in_rangestartxstopifin_range:# 检查x是否落在step的倍数上return(x-start)%step0else:in_rangestopxstartifin_range:return(x-start)%step0returnFalse3.3 支持索引和切片rrange(0,100,3)# 0, 3, 6, ..., 99# 索引——O(1)计算print(r[0])# 0print(r[1])# 3print(r[10])# 30print(r[-1])# 99 —— 最后一个# 切片——返回新的range对象也是O(1)print(r[5:15])# range(15, 45, 3)print(r[:5])# range(0, 15, 3)print(r[::2])# range(0, 100, 6) —— 步长加倍# 验证切片结果print(list(r[5:15]))# [15, 18, 21, 24, 27, 30, 33, 36, 39, 42]print(list(r[:5]))# [0, 3, 6, 9, 12]print(list(r[::2]))# [0, 6, 12, 18, ...]# 支持 reversed()print(list(reversed(r))[:5])# [99, 96, 93, 90, 87]# 支持 ! 比较r1range(0,10,2)r2range(0,10,2)r3range(0,9,2)print(r1r2)# True —— 相同参数print(r1r3)# False四、range()的实用技巧4.1 常用模式# 模式1重复N次for_inrange(10):print(Hello)# 打印10次# 模式2生成N个元素的列表squares[i**2foriinrange(1,11)]print(squares)# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]# 模式3倒序两种方式# 方式A使用reversed()foriinreversed(range(10)):print(i,end )# 9 8 7 6 5 4 3 2 1 0# 方式B使用负步长foriinrange(9,-1,-1):print(i,end )# 9 8 7 6 5 4 3 2 1 0# 模式4跳过foriinrange(0,100,10):print(i,end )# 0 10 20 30 40 50 60 70 80 90# 模式5并行遍历多个列表避免range(len())names[张三,李四,王五]scores[85,92,78]# ❌ 不推荐foriinrange(len(names)):print(f{names[i]}:{scores[i]})# ✅ 推荐forname,scoreinzip(names,scores):print(f{name}:{score})4.2 range()的数学应用# 生成等差数列defarithmetic_sequence(start,end,step):生成等差数列returnlist(range(start,end(1ifstep0else-1),step))print(arithmetic_sequence(1,10,2))# [1, 3, 5, 7, 9]print(arithmetic_sequence(10,1,-3))# [10, 7, 4, 1]# 生成乘法表foriinrange(1,10):forjinrange(1,i1):print(f{j}×{i}{i*j},end\t)print()# 斐波那契数列利用range的迭代次数控制deffibonacci(n):生成前n个斐波那契数result[]a,b0,1for_inrange(n):result.append(a)a,bb,abreturnresultprint(fibonacci(10))# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]# 分页数据生成defpaginate(items,page_size):将数据分页pages[]foriinrange(0,len(items),page_size):pages.append(items[i:ipage_size])returnpages datalist(range(25))pagespaginate(data,10)fori,pageinenumerate(pages,1):print(f第{i}页:{page})4.3 range()与列表索引的高级配合# 当你确实需要索引时range(len())是可以的# 但通常在特定的场景下# 场景1修改原列表的元素numbers[1,2,3,4,5]foriinrange(len(numbers)):numbers[i]numbers[i]**2print(numbers)# [1, 4, 9, 16, 25]# 场景2操作相邻元素data[3,1,4,1,5,9,2,6]foriinrange(len(data)-1):ifdata[i]data[i1]:print(f下降:{data[i]}→{data[i1]}at index{i})# 场景3从后往前处理stack[a,b,c,d,e]foriinrange(len(stack)-1,-1,-1):print(f弹出:{stack[i]})# 场景4跳跃处理datalist(range(20))# 每3个元素取一个foriinrange(0,len(data),3):print(data[i],end )# 0 3 6 9 12 15 18五、range() vs 其他迭代方式5.1 选择合适的迭代方式data[apple,banana,orange,grape,kiwi]# 1. 只需要值 → 直接迭代foritemindata:print(item)# 2. 需要索引和值 → enumerate()fori,iteminenumerate(data):print(f{i}:{item})# 3. 只需要索引 → range(len())foriinrange(len(data)):print(f索引:{i})# 4. 需要修改原列表 → range(len())foriinrange(len(data)):data[i]data[i].upper()# 5. 需要跳步 → range() 配合步长或切片foriinrange(0,len(data),2):print(data[i])# 或者切片 直接迭代foritemindata[::2]:print(item)# 6. 需要反向 → reversed() 或 range(len()-1, -1, -1)foriteminreversed(data):print(item)# 7. 需要特定范围内的迭代 → range(start, stop)foriinrange(5,10):print(i)5.2 range vs list 内存对比importsys# range对象的优势在循环中体现# for i in range(10000000): # 只占用约48字节# pass# for i in list(range(10000000)): # 占用约80MB不要这样做# pass# 对比不同大小的range和listsizes[100,10000,1000000]forninsizes:rrange(n)llist(r)print(fn{n:10,}: range{sys.getsizeof(r):6}bytes, list{sys.getsizeof(l):12,}bytes)六、实战案例6.1 进度条模拟器importtimedefsimulate_progress(total,desc处理中):模拟进度条 —— range控制迭代bar_length30foriinrange(total1):percenti/total filledint(bar_length*percent)bar█*filled░*(bar_length-filled)print(f\r{desc}|{bar}|{percent:.0%}({i}/{total}),end)time.sleep(0.05)print()# 换行# simulate_progress(50, 下载中)6.2 批量任务处理器classBatchProcessor:批量任务处理器 —— range控制批处理def__init__(self,items,batch_size100):self.itemsitems self.batch_sizebatch_size self.results[]defprocess(self,worker_func):分批处理所有元素totallen(self.items)batches(totalself.batch_size-1)//self.batch_sizeforbatch_idxinrange(batches):startbatch_idx*self.batch_size endmin(startself.batch_size,total)batchself.items[start:end]print(f处理批次{batch_idx1}/{batches}f(元素{start}-{end-1}, 共{len(batch)}个))# 处理批次batch_results[worker_func(item)foriteminbatch]self.results.extend(batch_results)# 模拟批次间暂停time.sleep(0.1)print(f\n✅ 全部处理完成:{len(self.results)}个结果)returnself.results# 使用# processor BatchProcessor(range(1000), batch_size150)# results processor.process(lambda x: x ** 2)七、本章小结✅ 本文我们全面掌握了range()函数三种参数形式range(stop)从0到stop-1range(start, stop)从start到stop-1range(start, stop, step)以step步长递增/递减。高效特性惰性计算不存储所有元素、O(1)成员检查、支持索引和切片、内存占用极低约48字节。实用技巧重复N次用for _ in range(n)、倒序用reversed(range(n))或range(n-1, -1, -1)、跳步用步长参数。何时用range(len())修改原列表元素、操作相邻元素、跳跃遍历——这些场景下range(len())是合理的。但对于简单的遍历值直接用for item in items更Pythonic。range()是for循环的最佳搭档理解它的每一个参数和特性能让你写出更高效、更优雅的循环代码。⌨️ 下一篇文章我们将学习enumerate()——让带索引的遍历更加优雅。
Python基础:range()函数的参数详解与高效用法
Python基础range()函数的参数详解与高效用法一、开篇for循环的灵魂伴侣在Python中range()函数几乎和for循环形影不离。每当你需要重复N次或遍历一段数字时就会用到它foriinrange(10):print(i)# 0到9foriinrange(5,10):print(i)# 5到9foriinrange(0,10,2):print(i)# 0, 2, 4, 6, 8看起来简单但range()有一些不太广为人知的细节它是惰性计算的、它支持负数步长、它可以用in做O(1)的成员检查、它甚至可以和len()配合使用。⌨️ 更重要的很多Python初学者滥用range(len(...))而错过了更Pythonic的迭代方式。今天就来彻底掌握range()。 全文将围绕三个核心问题展开range()的参数到底怎么用、它为什么高效、什么时候该用什么时候不该用。二、range()的三种参数形式2.1 单参数range(stop)# range(stop) —— 从0到stop-1# 等价于数学中的 [0, stop)foriinrange(5):print(i,end )# 输出: 0 1 2 3 4# 转换为列表查看内容print(list(range(5)))# [0, 1, 2, 3, 4]print(list(range(1)))# [0]print(list(range(0)))# [] —— stop0 生成空range# print(list(range(-5))) # [] —— 负数也一样是空的# 记忆技巧range(n) 产生 n 个数字从0到n-12.2 双参数range(start, stop)# range(start, stop) —— 从start到stop-1# 等价于数学中的 [start, stop)foriinrange(5,10):print(i,end )# 输出: 5 6 7 8 9print(list(range(3,8)))# [3, 4, 5, 6, 7]print(list(range(-3,3)))# [-3, -2, -1, 0, 1, 2]print(list(range(10,5)))# [] —— start stop空range# 计算range中的元素个数stop - start当start stop时print(len(range(5,10)))# 5print(len(range(-3,3)))# 62.3 三参数range(start, stop, step)# range(start, stop, step) —— 从start开始每次加step直到达到或超过stop# 正步长print(list(range(0,10,2)))# [0, 2, 4, 6, 8] —— 偶数print(list(range(1,10,2)))# [1, 3, 5, 7, 9] —— 奇数print(list(range(0,100,10)))# [0, 10, 20, 30, ..., 90]# 负步长倒序print(list(range(10,0,-1)))# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]print(list(range(10,0,-2)))# [10, 8, 6, 4, 2]print(list(range(5,-5,-1)))# [5, 4, 3, 2, 1, 0, -1, -2, -3, -4]# ⚠️ step不能为0# list(range(0, 10, 0)) # ValueError: range() arg 3 must not be zero# 步长的符号决定方向# step 0: 从start向stop增长要求 start stop 才有元素# step 0: 从start向stop减小要求 start stop 才有元素print(list(range(0,10,2)))# 有元素: start(0) stop(10), step(2) 0print(list(range(10,0,2)))# 空start(10) stop(0) 但 step(2) 0print(list(range(10,0,-2)))# 有元素: start(10) stop(0), step(-2) 02.4 参数名称# range的参数也可以使用关键字r1range(stop5)r2range(start2,stop8)r3range(start10,stop0,step-2)# 但不常用的组合可能无法使用关键字# range(start0, 10) # SyntaxError位置参数不能在关键字参数后面# 建议大多数情况下使用位置参数更简洁# range(5) 而不是 range(stop5)# range(2, 8) 而不是 range(start2, stop8)三、range对象的高效特性3.1 惰性计算Lazy Evaluation# range() 不创建包含所有元素的列表# 它是一个序列对象按需生成值importsys# range对象非常小rrange(1000000)# 一百万个元素lstlist(range(1000000))# 一百万个元素的列表print(frange对象大小:{sys.getsizeof(r):,}bytes)# 约48 bytesprint(f列表对象大小:{sys.getsizeof(lst):,}bytes)# 约8,000,000 bytes# range只在迭代时计算下一个值不存储所有值# 这是它比列表高效得多的原因# range对象的重要特性rrange(10)print(len(r))# 10 —— O(1)计算print(r[0])# 0 —— 通过公式计算print(r[5])# 5print(r[-1])# 9 —— 支持负索引# print(r[100]) # IndexError3.2 O(1)的成员检查# range的 in 操作是O(1)的# 它通过数学公式检查而不是遍历所有元素rrange(0,1000000,2)print(500000inr)# TrueO(1)时间print(500001inr)# FalseO(1)时间# 对比列表的O(n)检查importtimeit lstlist(r)t_rangetimeit.timeit(lambda:999998inr,number10000)t_listtimeit.timeit(lambda:999998inlst,number10000)print(frange in:{t_range:.4f}s)# 极快print(flist in:{t_list:.4f}s)# 慢得多# range的in检查原理defexplain_range_contains(r,x):解释range如何做成员检查start,stop,stepr.start,r.stop,r.stepifstep0:in_rangestartxstopifin_range:# 检查x是否落在step的倍数上return(x-start)%step0else:in_rangestopxstartifin_range:return(x-start)%step0returnFalse3.3 支持索引和切片rrange(0,100,3)# 0, 3, 6, ..., 99# 索引——O(1)计算print(r[0])# 0print(r[1])# 3print(r[10])# 30print(r[-1])# 99 —— 最后一个# 切片——返回新的range对象也是O(1)print(r[5:15])# range(15, 45, 3)print(r[:5])# range(0, 15, 3)print(r[::2])# range(0, 100, 6) —— 步长加倍# 验证切片结果print(list(r[5:15]))# [15, 18, 21, 24, 27, 30, 33, 36, 39, 42]print(list(r[:5]))# [0, 3, 6, 9, 12]print(list(r[::2]))# [0, 6, 12, 18, ...]# 支持 reversed()print(list(reversed(r))[:5])# [99, 96, 93, 90, 87]# 支持 ! 比较r1range(0,10,2)r2range(0,10,2)r3range(0,9,2)print(r1r2)# True —— 相同参数print(r1r3)# False四、range()的实用技巧4.1 常用模式# 模式1重复N次for_inrange(10):print(Hello)# 打印10次# 模式2生成N个元素的列表squares[i**2foriinrange(1,11)]print(squares)# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]# 模式3倒序两种方式# 方式A使用reversed()foriinreversed(range(10)):print(i,end )# 9 8 7 6 5 4 3 2 1 0# 方式B使用负步长foriinrange(9,-1,-1):print(i,end )# 9 8 7 6 5 4 3 2 1 0# 模式4跳过foriinrange(0,100,10):print(i,end )# 0 10 20 30 40 50 60 70 80 90# 模式5并行遍历多个列表避免range(len())names[张三,李四,王五]scores[85,92,78]# ❌ 不推荐foriinrange(len(names)):print(f{names[i]}:{scores[i]})# ✅ 推荐forname,scoreinzip(names,scores):print(f{name}:{score})4.2 range()的数学应用# 生成等差数列defarithmetic_sequence(start,end,step):生成等差数列returnlist(range(start,end(1ifstep0else-1),step))print(arithmetic_sequence(1,10,2))# [1, 3, 5, 7, 9]print(arithmetic_sequence(10,1,-3))# [10, 7, 4, 1]# 生成乘法表foriinrange(1,10):forjinrange(1,i1):print(f{j}×{i}{i*j},end\t)print()# 斐波那契数列利用range的迭代次数控制deffibonacci(n):生成前n个斐波那契数result[]a,b0,1for_inrange(n):result.append(a)a,bb,abreturnresultprint(fibonacci(10))# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]# 分页数据生成defpaginate(items,page_size):将数据分页pages[]foriinrange(0,len(items),page_size):pages.append(items[i:ipage_size])returnpages datalist(range(25))pagespaginate(data,10)fori,pageinenumerate(pages,1):print(f第{i}页:{page})4.3 range()与列表索引的高级配合# 当你确实需要索引时range(len())是可以的# 但通常在特定的场景下# 场景1修改原列表的元素numbers[1,2,3,4,5]foriinrange(len(numbers)):numbers[i]numbers[i]**2print(numbers)# [1, 4, 9, 16, 25]# 场景2操作相邻元素data[3,1,4,1,5,9,2,6]foriinrange(len(data)-1):ifdata[i]data[i1]:print(f下降:{data[i]}→{data[i1]}at index{i})# 场景3从后往前处理stack[a,b,c,d,e]foriinrange(len(stack)-1,-1,-1):print(f弹出:{stack[i]})# 场景4跳跃处理datalist(range(20))# 每3个元素取一个foriinrange(0,len(data),3):print(data[i],end )# 0 3 6 9 12 15 18五、range() vs 其他迭代方式5.1 选择合适的迭代方式data[apple,banana,orange,grape,kiwi]# 1. 只需要值 → 直接迭代foritemindata:print(item)# 2. 需要索引和值 → enumerate()fori,iteminenumerate(data):print(f{i}:{item})# 3. 只需要索引 → range(len())foriinrange(len(data)):print(f索引:{i})# 4. 需要修改原列表 → range(len())foriinrange(len(data)):data[i]data[i].upper()# 5. 需要跳步 → range() 配合步长或切片foriinrange(0,len(data),2):print(data[i])# 或者切片 直接迭代foritemindata[::2]:print(item)# 6. 需要反向 → reversed() 或 range(len()-1, -1, -1)foriteminreversed(data):print(item)# 7. 需要特定范围内的迭代 → range(start, stop)foriinrange(5,10):print(i)5.2 range vs list 内存对比importsys# range对象的优势在循环中体现# for i in range(10000000): # 只占用约48字节# pass# for i in list(range(10000000)): # 占用约80MB不要这样做# pass# 对比不同大小的range和listsizes[100,10000,1000000]forninsizes:rrange(n)llist(r)print(fn{n:10,}: range{sys.getsizeof(r):6}bytes, list{sys.getsizeof(l):12,}bytes)六、实战案例6.1 进度条模拟器importtimedefsimulate_progress(total,desc处理中):模拟进度条 —— range控制迭代bar_length30foriinrange(total1):percenti/total filledint(bar_length*percent)bar█*filled░*(bar_length-filled)print(f\r{desc}|{bar}|{percent:.0%}({i}/{total}),end)time.sleep(0.05)print()# 换行# simulate_progress(50, 下载中)6.2 批量任务处理器classBatchProcessor:批量任务处理器 —— range控制批处理def__init__(self,items,batch_size100):self.itemsitems self.batch_sizebatch_size self.results[]defprocess(self,worker_func):分批处理所有元素totallen(self.items)batches(totalself.batch_size-1)//self.batch_sizeforbatch_idxinrange(batches):startbatch_idx*self.batch_size endmin(startself.batch_size,total)batchself.items[start:end]print(f处理批次{batch_idx1}/{batches}f(元素{start}-{end-1}, 共{len(batch)}个))# 处理批次batch_results[worker_func(item)foriteminbatch]self.results.extend(batch_results)# 模拟批次间暂停time.sleep(0.1)print(f\n✅ 全部处理完成:{len(self.results)}个结果)returnself.results# 使用# processor BatchProcessor(range(1000), batch_size150)# results processor.process(lambda x: x ** 2)七、本章小结✅ 本文我们全面掌握了range()函数三种参数形式range(stop)从0到stop-1range(start, stop)从start到stop-1range(start, stop, step)以step步长递增/递减。高效特性惰性计算不存储所有元素、O(1)成员检查、支持索引和切片、内存占用极低约48字节。实用技巧重复N次用for _ in range(n)、倒序用reversed(range(n))或range(n-1, -1, -1)、跳步用步长参数。何时用range(len())修改原列表元素、操作相邻元素、跳跃遍历——这些场景下range(len())是合理的。但对于简单的遍历值直接用for item in items更Pythonic。range()是for循环的最佳搭档理解它的每一个参数和特性能让你写出更高效、更优雅的循环代码。⌨️ 下一篇文章我们将学习enumerate()——让带索引的遍历更加优雅。