将以上一篇文章中二维向量Vector2d类为基础(文章链接:进阶:编写符合Python风格的对象_lijiachang8的博客-CSDN博客),定义表示多维向量的Vector类。这个类的行为与Python中标准的不可变扁平序列一样。
Vector类:多维向量类
多维向量要让__init__方法接收任意个参数,但是,序列类型的构造方法最好接收可迭代对象为参数,因为所有的内置序列类型都是这样的(接收可迭代对象)。
示例,一个多维向量类
import math
from array import array
import reprlib
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):
class_name = type(self).__name__ # 自省类名
components = reprlib.repr(self.__components) # 数据过多时,省略号展示 array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...])
components = components[components.find('['):-1] # 截取字符串成 [0.0, 1.0, 2.0, 3.0, 4.0, ...]
return '{}({})'.format(class_name, components)
def __str__(self):
return str(tuple(self)) # 从可迭代对象可以轻松得到一个元组,然后转成字符串。tuple( iterable ),所以会调用__iter__
def __eq__(self, other):
return tuple(self) == tuple(other) # 构建成元组,可以快速比较所有分量。
def __bytes__(self):
"""向量类转换为bytes"""
return bytes([ord(self.type_code)]) + bytes(self.__components) # b'd' + b'\x00...'
def __abs__(self):
return math.sqrt(sum(x * x for x in self.__components)) # math.sqrt()求平方根。先用sum求每个分量的平方之和,然后开平方
def __bool__(self):
return bool(abs(self)) # 判断模是否为0
@classmethod
def from_bytes(cls, bytes_):
"""bytes转换为向量类"""
type_code = chr(bytes_[0])
memy = memoryview(bytes_[1:]).cast(type_code)
return cls(memy)
知识点:
- reprlib.repr() 这个函数用于生产大型结构或递归结构的安全表示形式,它会限制输出字符串的长度,用省略号...表示截断的部分。
- components[components.find('['):-1] 中的components.find('[')是找到第一个[符号的索引,从而构成components[10:-1] 的切片。 其实可以简单是使用reprlib.repr(list(self.__compoents))返回,但是这样有点浪费性能,这样会产生一个新的列表,没必要的。
- 调用repr()函数(__repr__方法)的目的是为了调试,因此绝对不能出现异常,让用户能够识别目标对象。
- from_bytes方法的返回return cls(memy)中memy已经不需要拆包了,因为Vector类可以接受可迭代对象。
协议和鸭子类型
什么是协议:
在面向对象编程中,协议是非正式的接口,只在文档中定义,在代码中不定义。
例如,Python的序列协议只需要实现__len__和__getitem__方法。任何类,只要使用标准的签名和语义实现这两个方法,那么就是序列类型。也就是说,想实现一个序列类型,是不是序列的子类无关紧要,重要提供了所需的方法即可。
示例,实现纸牌类,是序列,但是没有继承。
import collections
Card = collections.namedtuple('Card', 'rank suit')
class FrenchDeck:
ranks = [str(x) for x in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split() # 花色
def __init__(self):
self._cards = [Card(rank, suit) for rank in self.ranks for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, item):
return self._cards[item]
def __repr__(self):
return str(self._cards)
cards = FrenchDeck()
print(cards)
print(len(cards))
print(cards[0])
打印结果
[Card(rank='2', suit='spades'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='clubs'), Card(rank='2', suit='hearts'), Card(rank='3', suit='spades'), Card(rank='3', suit='diamonds'), Card(rank='3', suit='clubs'), Card(rank='3', suit='hearts'), Card(rank='4', suit='spades'), Card(rank='4', suit='diamonds'), Card(rank='4', suit='clubs'), Card(rank='4', suit='hearts'), Card(rank='5', suit='spades'), Card(rank='5', suit='diamonds'), Card(rank='5', suit='clubs'), Card(rank='5', suit='hearts'), Card(rank='6', suit='spades'), Card(rank='6', suit='diamonds'), Card(rank='6', suit='clubs'), Card(rank='6', suit='hearts'), Card(rank='7', suit='spades'), Card(rank='7', suit='diamonds'), Card(rank='7', suit='clubs'), Card(rank='7', suit='hearts'), Card(rank='8', suit='spades'), Card(rank='8', suit='diamonds'), Card(rank='8', suit='clubs'), Card(rank='8', suit='hearts'), Card(rank='9', suit='spades'), Card(rank='9', suit='diamonds'), Card(rank='9', suit='clubs'), Card(rank='9', suit='hearts'), Card(rank='10', suit='spades'), Card(rank='10', suit='diamonds'), Card(rank='10', suit&