Python Slice对象到Sequence序列对象

问题背景

最近在攻Numpy包,发现以下特别让我很疑惑,因为从python基础而来,终将又回到python中去:

import  numpy as np
a = np.array([1,2,3,4])
b = a[1:2] 
c = a[1]

print('the type of b is:{},value is {}'.format(type(b),b)) #
print('the type of c is:{},value is {}'.format(type(c),c))

output:
the type of b is:<class 'numpy.ndarray'>,value is [2] 
the type of c is:<class 'numpy.int64'>,value is 2
  • b、c数据类型分别为ndarray类、int类,value上b多了一个中括号;
  • 经列表进行切片(slice)以及索引以后,返回不同的对象,分别是列表 list、 integer,在经过查询python doc以后发现这其中存在slice对象 slice对象
    • Slice对象:创建方式:Slice(start,stop,step) 或者 var[start:stop:step] 中间使用冒号,代表var对象内部中在使用Slice对象;
    • 不过在此之前需要提出一点:像序列sequence(元组、列表、字符、slice)都是通过方法__getItem__方法通过整数索引对元素进行访问,slice也是如此。以下是自定义序列(当然协议实现协议还需要有__len__,不过此处用不到)。
d =slice(1,4,1) # 1 2 3 返回3个元素
a = [1,2,3,4]
c = a[d]  #取3个元素 #list列表的索引只能为int或slice对象 否则出现: TypeError: list indices must be integers or slices, not list 
c1 = a[1:4:1] #a作为列表,使用a:b:c这种索引在内部会产生像上述 同样的slice对象
print(c) # 2,3,4
print(c1) # 2,3,4

#list索引接受索引整数和slice对象时分别返回对象是integer、 list对象。这就可以解释在numpy中np.array[1:2] 与np.array[1]维度分别是
a = [1,2,3,4] 
b = a[1] # b 返回值:1
c = slice(1)
a[c] #返回值:[1] 将slice对象c传入到a中所谓索引,返回[1] 与a[1:2]一致。

切片与__getitem__方法联系

  • [start:stop:step] 在python中就涉及到了切片操作 传递给__getitem__方法分别对应不同的参数,当索引是integer的时候,序列将索引当作元素在序列中位置,直接取数,涉及到切片的时候按照以下进行:
class Demo():
    def __getitem__(self, item):
        return item
de =Demo()
de[1]  # Out[3]: 1  整数索引 返回元素
de[1,2,3] # Out[4]: (1, 2, 3) 索引中存在逗号,返回元组
de[1:3:1] # Out[5]: slice(1, 3, 1) 索引中存在冒号: 返回slice对象

穿插点:slice对象、sequence对象

Slice对象:

  • 在官方定义中,Slice对象经常包含序列的一部分,这样肯定不太理解,换个说法,在方括号中 数字之间有冒号,这里面就存在切片 slice
  • 生成Slice类,slice(start,stop,step),或者slice(stop),上述例子已经给出,此处再多余说一个slice类的方法,indices(len)返回一个元组(start,stop,step)用于规整slice类中对于超出start、stop、step所定义区间的范围,换句话说超出区间的会被截掉:
slice(1,10).indices(5) 等同于 slice(1:5:1) 
slice(-2,None,None).indices(6) 等同于 slice(4,6,1),处理负数索引
'ABCDEFGH'[-3:20:1] 字符串总共长度只有8,索引内stop为20,超出长度,负数为逆序索引,正序索引即为长度+负数索引,step不变,所以前面切片等同于'ABCDEFGH'[5:8:1]

sequence对象

  • python中奉行一切皆对象,也存在许多鸭子类型,对于序列对象也不例外,只要实现了序列接口协议,我们也可以将其当作自定义序列对象:实现了__getitem__以及__len___
  • 当初也是在研究list[index]的时候看python发生了什么,后来了解到当使用 x[key]的时候,内部就会调用x.getitem(key)这一特殊方法,所以接下来需要去深入__getitem__ 方法
from array import array 
import numbers

class Demo():
      def __init__(self,temp):
            self.__temp = array.array('i',temp) #其中i作为typecode,代表初始化数组的数据类型是整型
      def __getitem__(self,index): 
            if isinstince(index,slice): #index是slice对象
                  return self.__class__(self.__temp[index])
            elseif isinstance(index,number.integral): #索引是整数 ,至于为何使用numbers.Integral 将在下文中作出解释
                  return slef.__temp[index]
            else:
                  msg = '{cls.__name} indices must be integers' 
                  raise TypeError(msg.format(cls=cls))
      def __len__(self):
            return len(self.__temp)
de = Demo(range(7))
de[1:3:1]  # Out: <__main__.Demo at 0x10ffffd68>  # 索引为slice对象的时候,返回Demo实例,无论是list还是numpy中的array,当其索引是slice的时候,返回均是其实实例(slice对象)
de[slice(1,3,1)] # Out: <__main__.Demo at 0x110013ef0> # 
de[1] # Out: 1 #整数索引,返回对应位置元素值
de['s'] # TypeError: Demo indices must be integers #非法索引 报错

番外知识点

+上述代码中使用了isinstance,一般来说我们是检查是否存在继承关系,在查看模块number.integral以及int类源代MRO关系如下:

import  numbers
import inspect

print(inspect.getmro(numbers.Integral))  # (<class 'numbers.Integral'>, <class 'numbers.Rational'>, <class 'numbers.Real'>, <class 'numbers.Complex'>, <class 'numbers.Number'>, <class 'object'>)
print(inspect.getmro(int)) # (<class 'int'>, <class 'object'>)
  • 在模块number中的integral类属于抽象类,因为Number继承自元类ABC,在integral源代码末尾将int注册为它的虚拟子类,这样int就可以去实现integral类中所定义的方法。官方对于虚拟子类重写isinstance方法
  • 所以isinstance也不只是检查继承,还有抽象类虚拟子类之间的关系。
原文地址:https://www.cnblogs.com/ivan09/p/14206409.html