序列的修改、散列和切片
Vector第1版(不可切片版):
from array import array import reprlib import math class Vector: type_code = 'd' def __init__(self,components): self._components = array(self.type_code,components) def __iter__(self): return iter(self._components) def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] return 'Vector({})'.format(components) def __str__(self): return str(tuple(self)) def __bytes__(self): return bytes([ord(self.type_code)]) + bytes(self._components) def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): return math.sqrt(x*x for x in self._components) def __bool__(self): return bool(abs(self)) @classmethod def frombytes(cls,octets): t_code = chr(octets[0]) comps = array(t_code) comps.frombytes(octets[1:]) return cls(comps) longlist = [i for i in range(100)] longVector = Vector(longlist) print('repr(longVector):',repr(longVector)) v = Vector([3.1,4.2]) print('str(v):',v) print('repr(v):',repr(v)) b = bytes(v) w = Vector.frombytes(b) print('repr(w):',repr(w)) print(v == w) 输出: repr(longVector): Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...]) str(v): (3.1, 4.2) repr(v): Vector([3.1, 4.2]) repr(w): Vector([3.1, 4.2]) True
Vector第2版(不完美切片版):
from array import array import reprlib import math class Vector: type_code = 'd' def __init__(self,components): self._components = array(self.type_code,components) def __iter__(self): return iter(self._components) def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] return 'Vector({})'.format(components) def __str__(self): return str(tuple(self)) def __bytes__(self): return bytes([ord(self.type_code)]) + bytes(self._components) def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): return math.sqrt(x*x for x in self._components) def __bool__(self): return bool(abs(self)) @classmethod def frombytes(cls,octets): t_code = chr(octets[0]) comps = array(t_code) comps.frombytes(octets[1:]) return cls(comps) # 定义下面两个方法以后,Vector类就支持简单切片 def __len__(self): return len(self._components) def __getitem__(self, idx): return self._components[idx] l = [i for i in range(100)] v = Vector(l) print('repr(longVector):',repr(v)) # []操作符调用__getitem__ print(v[0],v[-1]) # _components是一个array,所以切片返回array print(v[1:4]) 输出: repr(longVector): Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...]) 0.0 99.0 array('d', [1.0, 2.0, 3.0])
切片原理
- 切片背后与slice对象和indices对象有关
- 实际调用的是slice(start, stop, stride).indices(len)
- 给定长度为len的序列,计算起始和结尾的索引,以及步幅。超出边界的索引会被截掉。
class MySeq: def __getitem__(self, item): return item s = MySeq() l = [1,2,3,4,5] # 切片时传递给__getitem__的参数是slice对象 # 这里是slice(1, 4, None) print(s[1:4]) # slice(1, 4, 2) print(s[1:4:2]) # 在内置可迭代对象的__getitem__中,还要调用indices方法对start、stop、stride进行处理 # 步幅为正数,start=None自动替换成0 # stop超过len,自动替换成len # (0, 5, 2) print(slice(None, 10, 2).indices(5)) # 相应切片返回[1, 3, 5] print(l[:10:2]) # 步幅=None,自动替换成1 # 步幅为正数,start=-3自动替换成len-3 # stop=None自动替换成len # (2, 5, 1) print(slice(-3, None, None).indices(5)) # 相应切片返回[3, 4, 5] print(l[-3::]) # 步幅为负数,start=-3自动替换成len-3 # stop=None自动替换成-1 # (2, -1, -1) print(slice(-3, None, -1).indices(5)) # 相应切片返回[3, 2, 1] print(l[-3::-1]) # 从2开始,倒退到3,切片为空 # (2, 3, -1) print(slice(-3, 3, -1).indices(5)) # 相应切片返回[] print(l[-3:3:-1])
Vector第3版(完美切片版):
from array import array import reprlib import math import numbers class Vector: type_code = 'd' def __init__(self,components): self._components = array(self.type_code,components) def __iter__(self): return iter(self._components) def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] return 'Vector({})'.format(components) def __str__(self): return str(tuple(self)) def __bytes__(self): return bytes([ord(self.type_code)]) + bytes(self._components) def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): return math.sqrt(x*x for x in self._components) def __bool__(self): return bool(abs(self)) @classmethod def frombytes(cls,octets): t_code = chr(octets[0]) comps = array(t_code) comps.frombytes(octets[1:]) return cls(comps) # 定义下面两个方法以后,Vector类就支持简单切片 def __len__(self): return len(self._components) def __getitem__(self, idx): cls = type(self) if isinstance(idx,slice): # 把slice对象传递给_components的__getitem__函数 return cls(self._components[idx]) elif isinstance(idx,numbers.Integral): # 直接返回相应元素 return self._components[idx] else: msg = '{0} indices must be integers' raise TypeError(msg.format(cls.__name__)) l = [i for i in range(100)] v = Vector(l) print('repr(longVector):',repr(v)) # 传入整数,__getitem__返回相应元素 print(v[0],v[-1]) # 经过改造后,切片返回Vector对象 print(repr(v[1:4]))
Vector第4版(带hash版):
from array import array import reprlib import math import numbers import functools import operator class Vector: type_code = 'd' def __init__(self,components): self._components = array(self.type_code,components) def __iter__(self): return iter(self._components) def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] return 'Vector({})'.format(components) def __str__(self): return str(tuple(self)) def __bytes__(self): return bytes([ord(self.type_code)]) + bytes(self._components) def __eq__(self, other): # zip生成一个由元组构成的生成器,在Vector由大数据量的可迭代对象构成时,效率比较高 # all函数在每一个元素都为True时返回True return len(self)==len(other) and all(a==b for a,b in zip(self,other)) def __hash__(self): # reduce接受三个参数,第一个参数是一个可接受两个参数的函数 # 第二个参数是一个可迭代对象,第三个参数是初始值,如果可迭代对象为空,返回这个初始值 hashes = (hash(i) for i in self) return functools.reduce(operator.xor, hashes, 0) def __abs__(self): return math.sqrt(x*x for x in self._components) def __bool__(self): return bool(abs(self)) @classmethod def frombytes(cls,octets): t_code = chr(octets[0]) comps = array(t_code) comps.frombytes(octets[1:]) return cls(comps) # 定义下面两个方法以后,Vector类就支持简单切片 def __len__(self): return len(self._components) def __getitem__(self, idx): cls = type(self) if isinstance(idx,slice): # 把slice对象传递给_components的__getitem__函数 return cls(self._components[idx]) elif isinstance(idx,numbers.Integral): # 直接返回相应元素 return self._components[idx] else: msg = '{0} indices must be integers' raise TypeError(msg.format(cls.__name__)) shortcut = 'xyzt' # 动态存取属性 def __getattr__(self, item): cls = type(self) if len(item) == 1: pos = cls.shortcut.find(item) if 0 <= pos < len(self._components): return self._components[pos] else: raise AttributeError('{0} has no attribute {1}'.format(cls,item)) l = [3.1, 4.2] v = Vector(l) print('repr(v):',repr(v)) print('v.x:',v.x) print('hash(v):',hash(v))
输出:
repr(v): Vector([3.1, 4.2])
v.x: 3.1
hash(v): 384307168202284039