一、前言
本节主要讲解问题如下:
1.用python代码实现一个range函数
2.python遍历列表时删除元素的错误做法
3.python遍历列表时删除元素的正确做法
二、python代码实现一个range函数
相信接触过python语言的我们,肯定都会经常使用range函数,那么这个range函数内部到底是怎么实现的呢?
下面我来仿造python的内置range函数来实现一个简单的range函数,代码如下:
class Mrange(object):
def __init__(self,start,stop=None,step=None):
#模拟range从0开始的行为
if stop is None:
self.start = 0
self.stop = start -1
else:
self.start = start
self.stop = stop -1
self.step = step
# 保留原始参数(打印对象的时候要使用)
self.origin_start = self.start
self.origin_stop = self.stop + 1
self.origin_step = self.step
def __repr__(self):
"""
:return:显示表现(打印print时触发这个函数)
"""
print('触发__repr__方法')
if self.origin_step:
result = '{}({},{},{})'.format(
self.__class__.__name__,#获取类名
self.origin_start,
self.origin_stop,
self.origin_step
)
else:
result = '{}({},{})'.format(
self.__class__.__name__,
self.origin_start,
self.origin_stop,
)
return result
def __iter__(self):
return self
def __next__(self):
"""
:return: 返回当前数字
"""
result = self.start
#步长取默认值1
if self.step is None:
if self.start <= self.stop:
self.start += 1
else:
raise StopIteration #迭代器迭代完成会引发StopIteration异常
#按给的步长默认值1
else:
if self.start <= self.stop:
self.start += self.step
else:
raise StopIteration # 迭代器完成会引发StopIteration异常
return result
mrange = Mrange(2,10,1)
print(mrange)
for i in mrange: #因为for循环内部有异常处理机制,所以当迭代器迭代完成并不会抛出异常
print(i)
上述代码的具体逻辑不再说明,这里主要说明几个函数的触发时机:
__repr__函数:当执行print打印这个类的对象时触发
__iter__函数:这里返回的是一个迭代器对象(不理解的可以去看可迭代对象和迭代器对象的区别),这个函数一般在for循环遍历可迭代对象时触发
__next__函数:一般在for循环遍历时触发
以上,就实现了一个简单的步长只能为正数的range函数
为什么这里要说range呢?因为下面要用到,请看下面
三、python遍历列表时删除元素的错误做法
错误方式1:
a = [1,2,3,4,5,6,7,8]
for i in a:
if i > 5:
pass
else:
a.remove(i)
print(a)
# 打印结果 [2, 4, 6, 7, 8]
#错误原因:因为遍历的是原列表,每次删除都会对原列表造成长度减1,
#但是for遍历的下标还是按照原来的下标来遍历,所以会造成元素跳过
如下图:
错误方式2:
a = [1,2,3,4,5,6,7,8]
for i in range(len(a)):
print(len(a),i)
if a[i]>5:
pass
else:
a.remove(a[i])
print(a)
#报错:IndexError: list index out of range
从这里的索引越界报错可以看出,range(len(a))确定之后就算a的长度变化,这里的值也是固定的了
这里错误的原因是:遍历的时候按照原列表的长度来遍历,可是中间执行过程中原列表的长度变小,所以,产生了下标越界的问题
四、python遍历列表时删除元素的正确做法
方法一: filter方法实现
a = [1,2,3,4,5,6,7,8]
s = filter(lambda x:x>5,a)
for i in s:
print(i)
方法二: 列表生成式实现
a = [1,2,3,4,5,6,7,8]
b = [i for i in a if i>5]
print(b)
方法三: 倒序删除
a = [1,2,3,4,5,6,7,8]
for i in range(len(a)-1,-1,-1):
if a[i] > 5:
pass
else:
a.remove(a[i])
print(a)
因为列表总是“向前移动”,所以可以倒序遍历,即使后面的元素被修改了,还没有被遍历的元素和其坐标还是保持不变的
这里,本人画图水平不高,可以自己试试画图实现一次次遍历,就会发现规律