一、核心要义
1. 一元运算符重载
2.加法和乘法运算符重载
3.比较运算符重载
4.增量赋值运算符重载
二、代码示例
1、一元运算符重载
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/25 10:35
# @Author : Maple
# @File : 01-一元运算符.py
# @Software: PyCharm"""
为第10章的Vector类重载运算符 +(__pos__,实现效果: x = +x) 和 - (__neg__,实现效果 x = -x)"""
import functools
import math
import operator
import reprlib
from array import arrayclass Vector:typecode = 'd'shortcut_names = 'xyzt'def __init__(self, components):self._components = array(self.typecode, components)def __repr__(self):# 返回的components是str类型components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __str__(self):return str(tuple(self))def __iter__(self):return iter(self._components)def __bytes__(self):return (bytes([ord(self.typecode)]) + bytes(self._components))def __bool__(self):return bool(abs(self))def __abs__(self):return math.sqrt(sum(x * x for x in self._components))@classmethoddef frombytes(cls, octets):typecode = chr(octets[0])memv = memoryview(octets[1:]).cast(typecode)return cls(memv)def __len__(self):return len(self._components)def __getitem__(self, index):cls = type(self)if isinstance(index, slice):return cls(self._components[index])elif isinstance(index, numbers.Integral):return self._components[index]else:msg = '{cls.__name__} indices must be integers'return TypeError(msg.format(cls=cls))def __getattr__(self, item):# 只有当v.x实例不存在x属性时,才会调用getattrcls = type(self)if len(item) == 1:position = cls.shortcut_names.find(item)if 0 <= position < len(self._components):return self._components[position]msg = '{.__name__!r} object has not attribute {!r}'raise AttributeError(msg.format(cls, item))def __setattr__(self, name, value):cls = type(self)if len(name) == 1:# 限制修改'xyzt'单字母属性值if name in cls.shortcut_names:error = 'readonly attribute {attr_name!r}'elif name.islower():# 限制修改单字母(a-z)的属性值error = "can't set attributes 'a' to 'z' in {cls_name}!r"else:error = ''if error:msg = error.format(cls_name=cls, attr_name=name)raise AttributeError(msg)# 允许修改名字为其它值的属性super().__setattr__(name, value)def __eq__(self, other):# 如果分量太多,下面这种方式效率太低# return tuple(self) == tuple(other)if len(self) != len(other):return Falsefor x, y in zip(self, other):if x != y:return Falsereturn Truedef __hash__(self):# 生成一个迭代器hashes = (hash(x) for x in self._components)return functools.reduce(operator.xor, hashes, 0)# 等价于下面的写法# return functools.reduce(lambda x,y : x *y ,hashes,0)# - 运算符重载def __neg__(self):return Vector(-x for x in self)# + 运算符重载def __pos__(self):return Vector(x for x in self)if __name__ == '__main__':# 1. - 运算符重载测试v = Vector([1,2,3,4])print(-v) # (-1.0, -2.0, -3.0, -4.0)# 2. + 运算符重载测试print(v == +v ) # True
2、向量加法运算符重载
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/25 10:44
# @Author : Maple
# @File : 02-重载向量加法运算符.py
# @Software: PyCharm"""
1.对于序列类型,默认的加法运算行为是,比如 [1,2,3] + [1,1,1] = [1,2,3,1,1,1]
2.但实际我们预期的结果是 [1,2,3] + [1,1,1] = [2,3,4]
"""#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/25 10:35
# @Author : Maple
# @File : 01-一元运算符.py
# @Software: PyCharm
import itertools"""
为第10章的Vector类重载运算符 +(__pos__,实现效果: x = +x) 和 - (__neg__,实现效果 x = -x)"""
import functools
import math
import operator
import reprlib
from array import arrayclass Vector:typecode = 'd'shortcut_names = 'xyzt'def __init__(self, components):self._components = array(self.typecode, components)def __repr__(self):# 返回的components是str类型components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __str__(self):return str(tuple(self))def __iter__(self):return iter(self._components)def __bytes__(self):return (bytes([ord(self.typecode)]) + bytes(self._components))def __bool__(self):return bool(abs(self))def __abs__(self):return math.sqrt(sum(x * x for x in self._components))@classmethoddef frombytes(cls, octets):typecode = chr(octets[0])memv = memoryview(octets[1:]).cast(typecode)return cls(memv)def __len__(self):return len(self._components)def __getitem__(self, index):cls = type(self)if isinstance(index, slice):return cls(self._components[index])elif isinstance(index, numbers.Integral):return self._components[index]else:msg = '{cls.__name__} indices must be integers'return TypeError(msg.format(cls=cls))def __getattr__(self, item):# 只有当v.x实例不存在x属性时,才会调用getattrcls = type(self)if len(item) == 1:position = cls.shortcut_names.find(item)if 0 <= position < len(self._components):return self._components[position]msg = '{.__name__!r} object has not attribute {!r}'raise AttributeError(msg.format(cls, item))def __setattr__(self, name, value):cls = type(self)if len(name) == 1:# 限制修改'xyzt'单字母属性值if name in cls.shortcut_names:error = 'readonly attribute {attr_name!r}'elif name.islower():# 限制修改单字母(a-z)的属性值error = "can't set attributes 'a' to 'z' in {cls_name}!r"else:error = ''if error:msg = error.format(cls_name=cls, attr_name=name)raise AttributeError(msg)# 允许修改名字为其它值的属性super().__setattr__(name, value)def __eq__(self, other):# 如果分量太多,下面这种方式效率太低# return tuple(self) == tuple(other)if len(self) != len(other):return Falsefor x, y in zip(self, other):if x != y:return Falsereturn Truedef __hash__(self):# 生成一个迭代器hashes = (hash(x) for x in self._components)return functools.reduce(operator.xor, hashes, 0)# 等价于下面的写法# return functools.reduce(lambda x,y : x *y ,hashes,0)# - 运算符重载def __neg__(self):return Vector(-x for x in self)# + 运算符重载def __pos__(self):return Vector(x for x in self)def __add__(self,other):# 返回两个对象的配对元组迭代器try:pairs = itertools.zip_longest(self,other,fillvalue=0.0)return Vector(a + b for a,b in pairs)except TypeError:raise NotImplementeddef __radd__(self, other):# 调用方式other.__add__(self)# 注意不要漏returnreturn self + otherif __name__ == '__main__':# 1. - 运算符重载测试v = Vector([1,2,3,4])print(-v) # (-1.0, -2.0, -3.0, -4.0)# 2. + 运算符重载测试print(v == +v ) # True# 3. 加法运算重载测试"""基本流程1. Vector对象 + 另外一个可迭代对象,正常返回结果2. 非Vector类型的可迭代对象 + Vector对象,首先会调用add, 因为add方法要求第一个对象是Vector类型,所以会报错:can only concatenate tuple (not "Vector") to tuple抛出NotImplemented错误3. 程序会继续调用__radd__方法,之后就会调用Vector对象.__add__(other),然后正常返回结果"""print(v + (1,2,3,4)) #(2.0, 4.0, 6.0, 8.0)print((1,2,3,4) + v) #(2.0, 4.0, 6.0, 8.0)
3、向量乘法运算符重载
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/25 11:05
# @Author : Maple
# @File : 03-重载标量乘法运算符.py
# @Software: PyCharm
import functools
import itertools
import numbers
from _ast import operator
from array import arrayclass Vector:typecode = 'd'shortcut_names = 'xyzt'def __init__(self, components):self._components = array(self.typecode, components)def __repr__(self):# 返回的components是str类型components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __str__(self):return str(tuple(self))def __iter__(self):return iter(self._components)def __bytes__(self):return (bytes([ord(self.typecode)]) + bytes(self._components))def __bool__(self):return bool(abs(self))def __abs__(self):return math.sqrt(sum(x * x for x in self._components))@classmethoddef frombytes(cls, octets):typecode = chr(octets[0])memv = memoryview(octets[1:]).cast(typecode)return cls(memv)def __len__(self):return len(self._components)def __getitem__(self, index):cls = type(self)if isinstance(index, slice):return cls(self._components[index])elif isinstance(index, numbers.Integral):return self._components[index]else:msg = '{cls.__name__} indices must be integers'return TypeError(msg.format(cls=cls))def __getattr__(self, item):# 只有当v.x实例不存在x属性时,才会调用getattrcls = type(self)if len(item) == 1:position = cls.shortcut_names.find(item)if 0 <= position < len(self._components):return self._components[position]msg = '{.__name__!r} object has not attribute {!r}'raise AttributeError(msg.format(cls, item))def __setattr__(self, name, value):cls = type(self)if len(name) == 1:# 限制修改'xyzt'单字母属性值if name in cls.shortcut_names:error = 'readonly attribute {attr_name!r}'elif name.islower():# 限制修改单字母(a-z)的属性值error = "can't set attributes 'a' to 'z' in {cls_name}!r"else:error = ''if error:msg = error.format(cls_name=cls, attr_name=name)raise AttributeError(msg)# 允许修改名字为其它值的属性super().__setattr__(name, value)def __eq__(self, other):# 如果分量太多,下面这种方式效率太低# return tuple(self) == tuple(other)if len(self) != len(other):return Falsefor x, y in zip(self, other):if x != y:return Falsereturn Truedef __hash__(self):# 生成一个迭代器hashes = (hash(x) for x in self._components)return functools.reduce(operator.xor, hashes, 0)# 等价于下面的写法# return functools.reduce(lambda x,y : x *y ,hashes,0)# - 运算符重载def __neg__(self):return Vector(-x for x in self)# + 运算符重载def __pos__(self):return Vector(x for x in self)def __add__(self,other):# 返回两个对象的配对元组迭代器try:pairs = itertools.zip_longest(self,other,fillvalue=0.0)return Vector(a + b for a,b in pairs)except TypeError:raise NotImplementeddef __radd__(self, other):# 调用方式other.__add__(self)# 注意不要漏returnreturn self + otherdef __mul__(self, scalar):# scalar参数的值要是数字if isinstance(scalar,numbers.Real):return Vector(n * scalar for n in self)else:return NotImplementeddef __rmul__(self, scalar):return self * scalarif __name__ == '__main__':# 乘法运算符测试v = Vector([1,2,3])print(v * 2) # # (2.0, 4.0, 6print(v * True) # (1.0, 2.0, 3.0)from fractions import Fractionprint(v * Fraction(1,3)) # (0.3333333333333333, 0.6666666666666666, 1.0)
4、比较运算符重载
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/25 12:48
# @Author : Maple
# @File : 04-比较运算符.py
# @Software: PyCharmimport functools
import itertools
import math
import numbers
from _ast import operator
from array import arrayclass Vector:typecode = 'd'shortcut_names = 'xyzt'def __init__(self, components):self._components = array(self.typecode, components)def __repr__(self):# 返回的components是str类型components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __str__(self):return str(tuple(self))def __iter__(self):return iter(self._components)def __bytes__(self):return (bytes([ord(self.typecode)]) + bytes(self._components))def __bool__(self):return bool(abs(self))def __abs__(self):return math.sqrt(sum(x * x for x in self._components))@classmethoddef frombytes(cls, octets):typecode = chr(octets[0])memv = memoryview(octets[1:]).cast(typecode)return cls(memv)def __len__(self):return len(self._components)def __getitem__(self, index):cls = type(self)if isinstance(index, slice):return cls(self._components[index])elif isinstance(index, numbers.Integral):return self._components[index]else:msg = '{cls.__name__} indices must be integers'return TypeError(msg.format(cls=cls))def __getattr__(self, item):# 只有当v.x实例不存在x属性时,才会调用getattrcls = type(self)if len(item) == 1:position = cls.shortcut_names.find(item)if 0 <= position < len(self._components):return self._components[position]msg = '{.__name__!r} object has not attribute {!r}'raise AttributeError(msg.format(cls, item))def __setattr__(self, name, value):cls = type(self)if len(name) == 1:# 限制修改'xyzt'单字母属性值if name in cls.shortcut_names:error = 'readonly attribute {attr_name!r}'elif name.islower():# 限制修改单字母(a-z)的属性值error = "can't set attributes 'a' to 'z' in {cls_name}!r"else:error = ''if error:msg = error.format(cls_name=cls, attr_name=name)raise AttributeError(msg)# 允许修改名字为其它值的属性super().__setattr__(name, value)def __eq__(self, other):# 下面这种写法存在一个问题,比如Vector([1,2,3]) 和(1,2,3)会被判断成相等,但大部分情况下,我们应该是预期不相等的结果# return tuple(self) == tuple(other)# 改写:另外一个对象必须是Vector对象,才可能相等if isinstance(other,Vector):return (len(self) == len(other) and all( x == y for x in self for y in other))else:return NotImplementeddef __hash__(self):# 生成一个迭代器hashes = (hash(x) for x in self._components)return functools.reduce(operator.xor, hashes, 0)# 等价于下面的写法# return functools.reduce(lambda x,y : x *y ,hashes,0)# - 运算符重载def __neg__(self):return Vector(-x for x in self)# + 运算符重载def __pos__(self):return Vector(x for x in self)def __add__(self,other):# 返回两个对象的配对元组迭代器try:pairs = itertools.zip_longest(self,other,fillvalue=0.0)return Vector(a + b for a,b in pairs)except TypeError:raise NotImplementeddef __radd__(self, other):# 调用方式other.__add__(self)# 注意不要漏returnreturn self + otherdef __mul__(self, scalar):# scalar参数的值要是数字if isinstance(scalar,numbers.Real):return Vector(n * scalar for n in self)else:return NotImplementeddef __rmul__(self, scalar):return self * scalarif __name__ == '__main__':v = Vector([1,2,3])print(v == (1,2,3)) # False
5、增量赋值运算符重载
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/25 12:54
# @Author : Maple
# @File : 05-增量赋值运算符.py
# @Software: PyCharm
import abcclass Tombola(abc.ABC):@abc.abstractmethoddef load(self,iterable):"""从可迭代对象中加载元素"""@abc.abstractmethoddef pick(self):"""随机删除元素,然后将其返回如果实例为空,这个方法应该抛出LookupError"""def loaded(self):"""如果至少有一个元素,则返回True,否则返回False"""return bool(self.inspect())def inspect(self):"""返回一个有序元组,由当前元素构成"""items = []while True:try:items.append(self.pick())except LookupError:breakself.load(items)return tuple(sorted(items))class AddableBingoCage(Tombola):def __init__(self,items):self._items = list(items)def pick(self):return self._items.pop()def __add__(self,other):# 另外一个对象必须是Tombola类型,才可以使用 + 运算符if isinstance(other,Tombola):return AddableBingoCage(self.inspect() + other.inspect())else:return NotImplementeddef __iadd__(self, other):if isinstance(other,Tombola):other_iterable = other.inspect()else:try:other_iterable = iter(other)except TypeError:cls_name = type(self).__name__msg = "right operand in += must be {!r} or an iterable"raise TypeError(msg.format(cls_name))self.load(other_iterable)# iadd是就地改变对象,所以要返回改变之后的对象本身return selfdef load(self,iterable):for i in iterable:self._items.append(i)def __iter__(self):return (x for x in self._items)# def __str__(self):# return str(tuple(self))def __repr__(self):return 'Vector{}'.format(tuple(self))def inspect(self):return self._itemsif __name__ == '__main__':# 1.就地修改(iadd)测试ab = AddableBingoCage([1,2,3])ab += [4,5]print(ab) # Vector(1, 2, 3, 4, 5)# ab += 1 #TypeError: right operand in += must be 'AddableBingoCage' or an iterable# 2.相加(add)测试#ab_new = ab + [1,2] # TypeError: unsupported operand type(s) for +: 'AddableBingoCage' and 'list'ab2 = AddableBingoCage([10,11])ab_new = ab + ab2print(ab_new) # Vector(1, 2, 3, 4, 5, 10, 11)