Python 触“类”旁通4|重载运算符之单链表的“加减乘除”

简介: Python 触“类”旁通4|重载运算符之单链表的“加减乘除”

通过前几篇的积累,节点类添加了创建、拼接和删除的功能,本篇尝试一下使用这些已定义过的函数方法快速重载链表间的算术运算:




加法

相当于用之前的 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 Node.build(self,*data)

完成的代码如下:

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 Node.build(*data,self)
'''
>>> 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__,以及众多的比较运算符,都可以用来重载对应的功能。

 

——*** 本篇完 ***——



目录
相关文章
|
4月前
|
人工智能 Python
Python 中的 `and`, `or`, `not` 运算符
本文介绍了 Python 中的逻辑运算符 `and`、`or` 和 `not` 的基本用法及其特性。这些运算符主要用于布尔运算,特别是在条件判断和循环中非常有用。文章详细解释了每个运算符的功能,例如 `and` 检查所有表达式是否为真,`or` 检查是否有任意一个表达式为真,`not` 用于取反。此外,还提到这些运算符支持短路特性,并可应用于非布尔值场景。掌握这些运算符有助于编写更高效、简洁的代码。
304 11
|
5月前
|
存储 Python
Python 中链表的个人理解
简介:本文介绍了Python中链表的基本组成及其操作实现。链表由`head`(头节点)、中间节点和`tail`(尾节点)三部分构成,每个节点通过`Node`类定义,包含`value`(值域)和`next`(指针域)。示例代码展示了链表的增删查功能,包括`add`(头部插入)、`append`(尾部插入)、`remove`(删除节点)、`search`(查找节点)及遍历方法。运行结果验证了链表操作的正确性。
|
5月前
|
人工智能 Python
[oeasy]python083_类_对象_成员方法_method_函数_function_isinstance
本文介绍了Python中类、对象、成员方法及函数的概念。通过超市商品分类的例子,形象地解释了“类型”的概念,如整型(int)和字符串(str)是两种不同的数据类型。整型对象支持数字求和,字符串对象支持拼接。使用`isinstance`函数可以判断对象是否属于特定类型,例如判断变量是否为整型。此外,还探讨了面向对象编程(OOP)与面向过程编程的区别,并简要介绍了`type`和`help`函数的用法。最后总结指出,不同类型的对象有不同的运算和方法,如字符串有`find`和`index`方法,而整型没有。更多内容可参考文末提供的蓝桥、GitHub和Gitee链接。
111 11
|
7月前
|
存储 Python
Python 实现单向链表,和单向链表的反转
链表是一种数据结构,每个节点存储相邻节点的位置信息。单链表中的节点仅存储下一节点的位置。通过Python实现单链表,定义`ListNode`类并关联节点可创建链表。例如,创建A-&gt;B-&gt;C的链表后,可通过反转函数`reverse`将链表反转为CBA。代码展示了如何实现和操作单链表。
133 6
Python 实现单向链表,和单向链表的反转
|
8月前
|
测试技术 Python
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
320 31
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
|
6月前
|
存储 C语言 Python
[oeasy]python077_int类型怎么用_整数运算_integer_进制转化_int类
本文主要讲解了Python中`int`类型的应用与特性。首先回顾了`int`词根的溯源,探讨了整型变量的概念及命名规则(如匈牙利命名法)。接着分析了整型变量在内存中的存储位置和地址,并通过`type()`和`id()`函数验证其类型和地址。还介绍了整型变量的运算功能,以及如何通过`int()`函数将字符串转化为整数,支持不同进制间的转换(如二进制转十进制)。此外,文章提及了关键字`del`的使用场景,对比了Python与C语言中`int`的区别,并总结了整型与字符串类型的差异,为后续深入学习奠定基础。
101 1
|
7月前
|
存储 算法 搜索推荐
Python 实现反转、合并链表有啥用?
大家好,我是V哥。本文介绍Python实现反转链表和合并链表的应用场景及代码实现。反转链表适用于时间序列数据展示、回文链表判断等;合并链表则用于大规模数据排序、数据库查询结果集合并等。通过迭代和递归方法实现反转链表,以及合并两个或多个有序链表的算法,帮助开发者解决实际问题。关注V哥,了解更多实用编程技巧。 先赞再看后评论,腰缠万贯财进门。
115 0
|
7月前
|
知识图谱 Python
Python入门:4.Python中的运算符
Python是一间强大而且便捷的编程语言,支持多种类型的运算符。在Python中,运算符被分为算术运算符、赋值运算符、复合赋值运算符、比较运算符和逻辑运算符等。本文将从基础到进阶进行分析,并通过一个综合案例展示其实际应用。
|
8月前
|
Python
探索 Python 中链表的实现:从基础到高级
链表是一种由节点组成的基础数据结构,每个节点包含数据和指向下一个节点的引用。本文通过Python类实现单向链表,详细介绍了创建、插入、删除节点等操作,并提供示例代码帮助理解。链表在处理动态数据时具有高效性,适用于大量数据变动的场景。文章为初学者提供了全面的入门指南,助你掌握链表的核心概念与应用。
356 0
|
9月前
|
数据采集 存储 XML
python实战——使用代理IP批量获取手机类电商数据
本文介绍了如何使用代理IP批量获取华为荣耀Magic7 Pro手机在电商网站的商品数据,包括名称、价格、销量和用户评价等。通过Python实现自动化采集,并存储到本地文件中。使用青果网络的代理IP服务,可以提高数据采集的安全性和效率,确保数据的多样性和准确性。文中详细描述了准备工作、API鉴权、代理授权及获取接口的过程,并提供了代码示例,帮助读者快速上手。手机数据来源为京东(item.jd.com),代理IP资源来自青果网络(qg.net)。

推荐镜像

更多