通过前几篇的积累,节点类添加了创建、拼接和删除的功能,本篇尝试一下使用这些已定义过的函数方法快速重载链表间的算术运算:
加法
相当于用之前的 push,append,cat 方法重载加法,也是非常恰当的。
加法重载的约定
当两个“加数”都为链表或节点时,后者拼接到前者尾部;当有一个非节点“加数”时,作为追加元素来处理。头插法为左加,追加法为右加,左、右加法分别用 __add__() 和 __radd__() 方法来重载。没有重载前,对Node类型做加法会报错的;如果只重载左加法不重载右加法,python 也只能识别 “node + 1” 为把1追加到node尾部;但不能识别出 “1 + node” 的用意,不支持的类型加法,并报错:TypeError: unsupported operand type(s) for +: 'int' and 'Node' 。
左加法
先增加一个左加法重载,用Node.build快速实现,不别写代码了:
def __add__(self,*data):
if self.val is None:
return Node.build(*data)
return
完成的代码如下:
class Node():
def __init__(self, value=None, Next=None):
self.val = value
self.next = Next
if type(self.next)==Node and self.next.val==None:
self.next=None
def __repr__(self):
return f'Node({self.val}->{self.next})'
def __str__(self):
return f'{self.val}->{self.next}'
def __len__(self):
if self.val is None:
return 0
length,ptr = 0,self
while ptr is not None:
length += 1
ptr = ptr.next
return length
def __eq__(self, other):
ptr1,ptr2 = self,other
if len(ptr1)!=len(ptr2):
return False
while ptr1 is not None:
if ptr1.val!=ptr2.val:
return False
ptr1 = ptr1.next
ptr2 = ptr2.next
else:
return True
def size(self):
return self.__len__()
@property
def length(self):
return self.size()
@property
def value(self):
return self.val
@property
def values(self):
ret,ptr = [],self
while ptr is not None:
ret.append(ptr.val)
ptr = ptr.next
return ret
def pprint(self):
items = [str(i) for i in self.values]
if items==[]:
print('None->None')
elif len(items)==1:
print(f'{items[0]}->None')
else:
print('->'.join(items)+'->None')
def isNode(node):
return isinstance(node,Node) and isinstance(node.next,(Node,type(None)))
def build(*data, split=True):
'''把数据转换成节点链表'''
lst,ret = [],Node()
for val in data:
if type(val) is str:
if not split:
lst.append(val)
continue
if str=='':
continue
try:
num = int(val)
lst.extend([int(_) for _ in val])
except:
lst.extend([_ for _ in val])
elif hasattr(val,'__iter__'):
lst.extend([_ for _ in val])
elif type(val) is Node:
if val is not None:
lst.extend(val.values)
else:
lst.append(val)
ret = Node()
for i in lst[::-1]:
ret = Node(i, ret)
return ret
def copy(node):
ret = Node()
ptr1,ptr2 = node,ret
while ptr1 is not None:
ptr2.next = Node(ptr1.val)
ptr1,ptr2 = ptr1.next,ptr2.next
return ret.next
def __add__(self,*data):
if self.val is None:
return Node.build(*data)
return Node.build(self,*data)
测试结果:
'''
>>> Node(1)+Node(2)
Node(1->2->None)
>>> Node(1)+Node(2)+3
Node(1->2->3->None)
>>> Node(1)+Node(2)+3+4
Node(1->2->3->4->None)
>>> 1+Node(2)
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
1+Node(2)
TypeError: unsupported operand type(s) for +: 'int' and 'Node'
>>>
'''
右加法
当节点在右面做加法时会报错,所以要增加一个右加法,再看效果:
def __radd__(self,*data):
return'''
>>> 1+Node(2)
Node(1->2->None)
>>> 1+Node(2,Node(3))
Node(1->2->3->None)
>>> 1+2+Node(3)
Node(3->3->None)
>>> (1,2)+Node(3)
Node(1->2->3->None)
>>>
"'
可以成功实现,但要注意:加法是从左到右逐个计算的,如上红色标注的会先计算1+2的;所以使用右加法时,想在左边用头插法追加多个元素时,一定要用括号把后面的先加;或者选用元组或列表等可迭代的对象作追加元素的容器; 或者索性在最左边给个空节点,变回到左加法。如下三种方式都可以:
>>> 1+(2+(3+Node(4)))
Node(1->2->3->4->None)
>>> [1,2,3]+Node(4)
Node(1->2->3->4->None)
>>> Node()+1+2+3+Node(4)
Node(1->2->3->4->None)
>>>
注意:左加法指“被加数”在左边,要加的“数”在右边;右加法反之。
自加法
即重载 __iadd__() 方法,默认的话自动继承自左加法,直接看测试:
>>> (1,2)+Node(3)
Node(1->2->3->None)
>>> node = (1,2)+Node(3)
>>> node += 4
>>> node
Node(1->2->3->4->None)
>>>
若要把它重载成右加法,则在类里__radd__后加一行“__iadd__ = __radd__”:
def __radd__(self,*data):
return Node.build(*data,self)
__iadd__ = __radd__
'''
>>> node = (1,2)+Node(3)
>>> node += 4
>>> node
Node(4->1->2->3->None)
>>>
'''
减法
相当于前几篇中定义的删除方法,但重载减法之前要检测一下“被减数”中包不包含“减数”。
包含运算 in
直接调用.values属性来“伪实现”这个功能。
def __contains__(self, other):
if type(other) is not Node:
return other in self.values
sub,subs = other.values,self.values
if len(sub)>len(subs): return False
subs=[subs[i:i+len(sub)] for i in range(len(subs)-len(sub)+1)]
return sub in subs
测试结果:
>>> node = Node()+1+2+3+4+5
>>> 1 in node
True
>>> 2 in node
True
>>> 5 in node
True
>>> 0 in node
False
>>> n1 = Node(2,Node(3))
>>> n2 = Node(2,Node(4))
>>> n1 in node
True
>>> n2 in node
False
>>>
左减法
链表间的减法就不像加法一样还要分左右,只有“被减数”减去“减数”,重载 __sub__即可:
def __sub__(self, other):
if other not in self:
raise BaseException("A-B Error:B not in A")
subs = self.values
if type(other) is not Node:
idx = subs.index(other)
subs = subs[:idx]+subs[idx+1:]
return Node.build(subs)
sub = other.values
tmp = [subs[i:i+len(sub)] for i in range(len(subs)-len(sub)+1)]
idx = tmp.index(sub)
subs = subs[:idx]+subs[idx+len(sub):]
return Node.build(subs)
'''
>>> a = Node()+range(1,10)
>>> a
Node(1->2->3->4->5->6->7->8->9->None)
>>> a - 2 -3 - 5 - 7
Node(1->4->6->8->9->None)
>>> a
Node(1->2->3->4->5->6->7->8->9->None)
>>> a - Node.build(3,4,5,6)
Node(1->2->7->8->9->None)
>>> a
Node(1->2->3->4->5->6->7->8->9->None)
>>> a -= Node.build(3,4,5,6)
>>> a
Node(1->2->7->8->9->None)
>>>
注:直接调用build()函数和values属性伪实现了减法效果。做减法前需要先判断一下“被减数”是否包含“减数”,包含则减去,不包含则会报错。
自减法
自减法__iadd__自动继承__add__不用另行写代码。
乘法
左乘法
乘法约定为数乘,即乘以一个整数n,链表的长度扩大n倍;另外尝试一下非数乘的情况:即当两个链表相乘时,用它们的数据域对应相乘的积做各节点的值。
def __mul__(self, other):
if type(other) is Node:
n1,n2 = self.values,other.values
product = [p[0]*p[1] for p in zip(n1,n2)]
return Node.build(product)
if other<0 or type(other) is not int:
raise TypeError("other is a non-negetive Integer")
if other==0:return Node()
ret = self.copy()
for _ in range(1,other):
self += ret
return self
__rmul__ = __mul__
'''
>>> a = Node() + range(1,3)
>>> a * 0
Node(None->None)
>>> a * 1
Node(1->2->None)
>>> a * 2
Node(1->2->1->2->None)
>>> a * 5
Node(1->2->1->2->1->2->1->2->1->2->None)
>>>
>>> 3 * a
Node(1->2->1->2->1->2->None)
>>> a
Node(1->2->None)
>>> a *= 5
>>> a
Node(1->2->1->2->1->2->1->2->1->2->None)
>>>
>>>
>>> a = Node() + range(1,8)
>>> b = Node(2) * 7
>>> a * b
Node(2->4->6->8->10->12->14->None)
>>> b * a
Node(2->4->6->8->10->12->14->None)
>>>
'''
右乘法
右乘也要重载,否则右乘时number * Node 会报错,添加一行:__rmul__ = __mul__ 即可。
自乘法
自乘法__imul__自动继承__mul__不用另行写代码。
除法
除法有两种:除和整除,分别是 __truediv__ 、__floordiv__,暂且把两种除法都看作是和整数作运算,并约定:链表除以一个整数n,即长度变为原来1/n。
def __truediv__(self, i):
if i<0 or type(i) is not int:
raise TypeError("i must be a positive Integer")
if i==1: return self
if self.next is None: return Node()
num = self.values
num = num[:len(num)//i]
return Node.build(num)
__floordiv__ = __truediv__
'''
>>> a = Node()+range(1,3)
>>> a *= 5
>>> a / 5
Node(1->2->None)
>>> a
Node(1->2->1->2->1->2->1->2->1->2->None)
>>> a/3
Node(1->2->1->None)
>>> a = Node()+range(1,3)
>>> a *= 6
>>> a
Node(1->2->1->2->1->2->1->2->1->2->1->2->None)
>>> a / 2
Node(1->2->1->2->1->2->None)
>>> a
Node(1->2->1->2->1->2->1->2->1->2->1->2->None)
>>> a // 3
Node(1->2->1->2->None)
>>> a
Node(1->2->1->2->1->2->1->2->1->2->1->2->None)
>>> a /= 3
>>> a
Node(1->2->1->2->None)
>>> a /= 2
>>> a
Node(1->2->None)
>>>
'''
同样 __itruediv__、__ifloordiv__ 自动继承不用另行写代码。
左移和右移
对一个整数来说,有按二进制位进行左移和右移的运算,相当于一个整数乘以或除以2;
对一个链表来说,调用上篇的 rotate(n) 方法来重载左移、右移运算(对应的内置方法 __lshift__ 和 __rshift__ ),模仿出来的效果还是蛮逼真的:
def __lshift__(self, n):
return self.rotate(n)
def __rshift__(self, n):
return self.rotate(-n)
测试效果:
>>> a = Node()+1+2+3+4+5
>>> a
Node(1->2->3->4->5->None)
>>> a>>1
Node(5->1->2->3->4->None)
>>> a
Node(1->2->3->4->5->None)
>>> a>>3
Node(3->4->5->1->2->None)
>>> a
Node(1->2->3->4->5->None)
>>>
参与运算的链表不发生变化,如要改变值的用 >>= 或 <<= 来赋值:
>>> b = Node()+1+2+3+4+5
>>> b
Node(1->2->3->4->5->None)
>>> b >>= 2
>>> b
Node(4->5->1->2->3->None)
>>> b >>= 1
>>> b
Node(3->4->5->1->2->None)
>>>
或者直接在代码中加入覆盖原链表的代码:
def __lshift__(self, n):
ret = self.rotate(n)
self.val,self.next = ret.val,ret.next
return ret
def __rshift__(self, n):
ret = self.rotate(-n)
self.val,self.next = ret.val,ret.next
return ret
'''
>>> node = Node.build(1,2,3,4,5)
>>> node
Node(1->2->3->4->5->None)
>>> node >> 1
Node(5->1->2->3->4->None)
>>> node >> 2
Node(3->4->5->1->2->None)
>>> node >> 3
Node(5->1->2->3->4->None)
>>> node
Node(5->1->2->3->4->None)
>>> node << 6
Node(1->2->3->4->5->None)
>>> node << 1
Node(2->3->4->5->1->None)
>>> node << 1
Node(3->4->5->1->2->None)
>>> node >> 2
Node(1->2->3->4->5->None)
>>> node
Node(1->2->3->4->5->None)
>>>
'''
本篇的内容纯粹是为了学习如何实现运算符的重载,除了包含运算in比较有用,其它都没什么实用性,可以试着用正规的遍历方法来实现它的功能。另外还有一些__neg__、 __pos__、 __abs__、__mod__、__divmod__、__pow__、__round__、__invert__、__and__ 、 __or__ 、 __xor__,以及众多的比较运算符,都可以用来重载对应的功能。
——*** 本篇完 ***——