一、模拟数值类型
有几种特殊方法可以使用户对象响应+、*等运算符,接下来通过实现一个二维向量类来对此进行说明。
一个简单的二维向量应实现向量加法、计算向量的模、向量的标量积的运算
import math
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# 用于表示对象的状态或内容
def __repr__(self):
return f'Vector({self.x!r}, {self.y!r})'
# 用于计算返回向量的模
def __abs__(self):
# math.hypot返回所有参数平方和的平方根
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
# 返回向量的和
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
# 返回向量的标量积
def __mul__(self, other):
return Vector(self.x * other, self.y * other)
v1 = Vector(2, 4)
v2 = Vector(2, 1)
print(v1 + v2)
# output: Vector(4, 5)
print(abs(Vector(3, 4)))
# output: 5.0
print(v1 * 3)
# output: Vector(6, 12)
示例使用了__repr__、__abs__、__add__、__mul__等特殊方法为Vector类实现了上述的运算。
补充:其中__add__、__mul__这两个方法创建并返回一个新的Vector实例,没有修改运算对象,只是读取self、other,这是中缀运算符的预期行为,即创建新对象,不修改运算对象。
二、对象的字符串表现形式
上述示例使用了特殊方法__repr__供内置函数repr()调用,获取对象的字符串表现形式,若不重构该方法,Vector类在显示台默认返回对象的类名称和对象的地址,如:
Vector类中__repr__方法中f字符串使用 !r 以标准的形式显示属性。
补充:
1.与__repr__方法形成对照的是__str__方法由内置函数str()调用,在背后供print函数使用,返回对终端用户友好的字符串。
2.repr()函数与str函数有什么区别呢?
1.repr()函数:将值转化为供解释器读取的字符串形式;str()函数:将值转化为适于人阅读的字符串的形式
2.命令行下直接输出对象调用的是repr方法;print输出调用的是str方法
3.除字符串类型外两者并无区别,对于字符串类型,repr方法会在外层多加一层引号,在eval操作时会特别有用
三、对象的布尔值
python有一个bool类型,在if、while语句的条件表达式,或and、or和not的运算对象这些需要布尔值的地方对对象进行处理,python会通过调用bool(x)来判断x的True或False。
默认情况下,用户定义的实例都是真值,除非实现了__bool__或__len__方法。bool(x)调用x.__bool__()以其返回的结果为准,若没有实现__bool__方法,也尝试调用x.__len__(),若该方法返回0,则bool函数返回False,否则返回True。
在上述示例中,因为__bool__方法必须返回一个bool值,我们使用bool(abs(self))将向量的模转换成bool值,如果向量模为0,则返回False,否则返回True。
四、实现容器
容器类型对特殊方法的使用:
1.最顶部的抽象基类均只有一个特殊方法,每一个容器类型均应实现如下事项:
(1)Iterable:__iter__,要支持for、拆包和其他迭代方式
(2)Sized:__len__,要支持内置函数len
(3)Container:__contains__,要支持in运算符
2.collection有3个重要的专用接口:
(1)Sequence:规范list和str等内置类型的接口
(2)Mapping:被dict、collections.defaultdict等实现
(3)Set:是set和frozenset两个内置类型的接口
注:只有Sequence实现了Reversible,因为序列支持以任意顺序排列内容,Mapping和Set不需要。