Python 索引切片迭代 类和对象

一、 __getitem__()和__setitem__()

        再python中,当对象被索引的时候会去调用__getitem__()和__setitem__()的魔法方法,主要有三个作用,我们依次用代码来演示。

1.1 响应单个下标的索引操作。

class C:
	def __getitem__(self,index):  #定义__getitem__()方法
		print(index)              #打印索引值
	
c = C()

c[2] 
# 2

1.2 支持代表范围的切片索引方式。

class C:
	def __getitem__(self,index):
		print(index)

c = C()

c[1:2]
# slice(1, 2, None)

1.2.1 slice()

        当进行切片索引的时候,会打印出 slice(1, 2, None);对于slice(),它是一个BIF函数,切片操作相当于是它的一个语法糖。

        BIF:(built-in-functions)顾名思义,就是Erlang【一种大规模并行处理环境的语言】内建函数。通常用来完成那些无法用Erlang完成的任务,比如将列表转换为元组,或者获取当前的日期和时间,完成这些操作的函数,我们称之为BIF函数。

s = "Hello World"
s[0:5]
# 'Hello'
s[slice(0,5)]
#'Hello'
s[6:]
# 'World'
s[slice(6,None)]
# 'World'
s[::2]
# 'HloWrd'
s[slice(None,None,2)]
# 'HloWrd'

        在上述代码中,我们用普通的切片操作和slice()函数进行比较,可以得到相同的结果,但是要注意的是,普通切片中没有写的位置,在slice()方法中要使用 'None' 来声明。   

              当为索引或者切片赋值的时候,就会被__setitem__()方法拦截。

class C:
	def __init__(self,data):
		self.data = data
	def __getitem__(self,index):
		return self.data[index]
	def __setitem__(self,index,value):
		self.data[index] = value

		
c = C([1,2,3,4,5])
c[2]
# 3
#单下标赋值
c[2] = 2
c[2]

# 2
# 切片赋值
c[0:5] = [5,4,3,2,1]
c[:]

#[5, 4, 3, 2, 1]

1.3 拦截与获取相关的操作。

        对于for循环,每次都会从可迭代对象中获取数据,这样的操作也会触发__getitem__()方法。

class C:
	def __init__(self,data):
		self.data = data
	def __getitem__(self,index):
		return self.data[index] * 2   #使得到的数据增加一倍


c = C([1,2,3,4,5])
for i in c:                        #通过for循环进行遍历
	print(i,end = " ")

	
# 2 4 6 8 10 

        我们得到了列表中扩大一倍的数据,说明使用for循环触发了__getitem__()方法,由此说明,__getitem__()会拦截与获取有关的操作。

二、for循环的原理

        对于for循环去访问__getitem__()是Python退而求其次的一种做法,真正实现对可迭代对象的魔法方法是__iter__()和__next__()。【当没有找到__iter__()和__next__()方法的时候,才回去寻找__getitem__()方法】

2.1 迭代器和可迭代对象

        如果一个对象定义了__iter__()方法,那么他就是一个可迭代对象,如果一个可迭代对象定义了__next__()魔法方法,那么他就是一个迭代器。

        比如列表,列表是一个可迭代对象,但是因为没有__next__()方法,所以他不是一个迭代器。

x = [1,2,3,4,5]
dir(x)        #可以看到,列表中有很多中方法,但是就是没有__next__()

'''
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

'''

next(x)          #调用next的时候会报错
'''
Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    next(x)
TypeError: 'list' object is not an iterator
'''

 2.2 for循环的原理

        当我们使用迭代工具for循环来操作时,首先会将可迭代对象放入内置的iter()中,以此得到一个相应的迭代器,继而获得所需的__next__()魔法方法。之后利用__next__()进行真正的迭代操作。

x = [1,2,3,4,5]
a = iter(x)   #将可迭代对象放入内置的 iter()中 获取迭代器
while True:
	try:
		i = a.__next__()      #通过__next__()进行迭代操作
	except StopIteration:  #当循环到最后结束程序
		break
	print(i,end = " ")

	
# 1 2 3 4 5 

三、创建一个迭代器

        根据上面描述的信息,我们可以创建一个属于自己的迭代器:

class DieDai:
	def  __init__(self,start,stop):
		self.value = start - 1  
#对于为什么减一,是因为在使用for循环的时候,会从下标为0开始访问,不想遗漏列表中第一个数据。      
		self.stop = stop
	def __iter__(self):
		return self   #本身就是一个迭代器,所以直接返回自身即可
	def __next__(self): 
		if self.value == self.stop:  #当迭代到最后一个元素时,停止
			raise StopIteration      #一但执行了raise语句,后面的语句就不再执行
		self.value += 1
		return self.value * 2

	
d = DieDai(1,5)
for i in d:
	print(i,end=' ')

	
# 2 4 6 8 10 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学者山羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值