特殊的分派机制和重载向量加法运算符+
导语
小编一直都觉自己公众号的排版很鸡肋,从这篇文章开始将使用了新的排版风格,还特意地做了一个卡通二维码(见文末),希望大家会喜欢(不要脸地假装有很多粉丝)。其实关于排版,小编要真心感谢一下景禹大佬的指导。好了,今天想跟大家谈谈如何重载运算符+,认真看完这篇文章,你将收获:
-
了解中缀运算符特殊方法的分派机制
-
了解向量类如何实现 __ add __ 方法
-
了解向量类如何实现 __ radd __ 方法
a+b背后如何调用特殊方法
大家都知道若a和b都是同类型序列,a+b可以实现序列接拼,若a和b都是int或者float等数值类型,a+b会实现数学上的加法,见示例1。
#示例1 a = (1,2) b = (4,5) print(a+b) #(1,2,4,5) a = [1,2] b = [4,5] print(a+b) #[1,2,4,5] a = b = 1 print(a+b) #2 a = b = 1.0 print(a+b) #2.0
但是,如果a和b是不同类型的序列,他们能否接拼成功呢?
要回答这个问题,我们先了解一下Python为中缀运算符特殊方法提供的特殊分派机制,其流程见下图。
对于表达式a+b,为了支持涉及不同类型的运算,Python解释器会执行以下几步操作。
- 如果a有__ add __ 方法,返回值不是NotImplemented,调用a.__ add __(b),然后返回结果
- 如果a没有__ add __ 方法,或者调用__ add __ 方法返回NotImplemented,检查b有没有__ radd __ 方法,如果有,调用b.__ radd __(a)方法后没有返回NotImplemented,返回结果。
- 如果b没有__ radd __ 方法,或者调用__ radd __方法返回NotImplemented,抛出TypeError,并在错误消息中指明操作类型不支持。
__ radd __ 是 __ add __的“反向”版本,Alex、Anna和Leo几位技术大佬喜欢称之为“右向”(right)特殊方法,因为他们都在右操作数上调用。
如果a和b是不同类型的序列,执行示例2的程序,会发生什么呢?
#示例2 a = (1,2) b = [3,4] print(a+b)
首先解释器会调用a.__ add __ (b)方法,因为a和b是不同类型的序列,所以返回NotImplemented。然后解释器检查b是否有b.__ radd __ (a) 方法,然后调用该方法,但是还是返回NotImplemented,最终抛出TypeError结果。
TypeError: can only concatenate tuple (not "list") to tuple
重载运算符+
关于重载运算符,有几点需要注意一下的:
- 不能重载内置类型的运算符,即list等这些内置类型的运算符不能重载。
- 不能新建运算符,只能重载现有的。
- 有些运算符不能重载,is、and、or和not(位运算&、|和~可以)。
现在我们尝试定义一个Vector类,见示例3。
#示例3 class Vector(object): def __init__(self,components): self.components = list(components)
如果我们不对Vector重载运算符+,两个Vector类相加抛出错误,见示例4
#示例4 v1 = Vector([1,2,3]) v2 = Vector([1,2,3]) print(v1+v2) #TypeError: unsupported operand type(s) for +: 'Vector' and 'Vector'
好了,我们现在马上对Vector重载运算符+吧,见示例5,但是我们不是实现接拼,而是实现数学上的加法,因为对于一个向量,接拼功能意义不大。
#示例5 class Vector(object): def __init__(self,components): self.components = list(components) def __iter__(self): return iter(self.components) def __str__(self): return str(self.components) def __add__(self,others): print("__add__") try: pairs = itertools.zip_longest(self,others,fillvalue=0.0) return Vector(a+b for a,b in pairs) except TypeError: return NotImplemented v1 = Vector([1,2,3]) v2 = Vector([1,2,3]) v3 = [1,2,3] print(v1+v2) print(v1+v3)
输出结果:
__add__ [2,4,6] __add__ [2,4,6]
从示例5中可以看到,解析器调用了__ add __方法实现了两个Vector或者Vector和具有数值元素的可迭代类型的相加。示例5能够实现V1+V3,那是否能实现V3+v1呢?见示例6。
#示例6 print(V3+V1) #TypeError: can only concatenate list (not "Vector") to list
示例6抛出错误了,当解释器执行v3.__ add __ (V1)时候,因为调用的是列表的__ add __ 方法,不能与自定义的类型相加,所以返回结果NotImplemented,并尝试执行V1.__ radd __ (V3)方法,但是在自定义的Vector类并没有实现该方法,所以最后还是抛出了TypeError。好了,现在尝试实现Vector类的__ radd __ 方法。在示例5的基础上添加示例7的代码。
#示例7 def __radd__(self,other): print("__radd__") return self+other
再次运行示例6,得到如下输出结果。
__radd__ __add__ [2, 4, 6]
当解释器执行V3.__ add __ (V1)返回NotImplemented之后,就会调用自定义类中的__ radd __ 方法, __ radd __ 方法把计算结果委托给 __ add __ ,事实上,任何可交换的运算符都可以这么做。
好啦,以上就是就是小编今天分享的内容,希望对你有用。
公众号:CVpython
专注于分享Python和计算机视觉,快点扫码关注我吧!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Serverless 实战:如何结合 NLP 实现文本摘要和关键词提取?
对文本进行自动摘要的提取和关键词的提取,属于自然语言处理的范畴。提取摘要的一个好处是可以让阅读者通过最少的信息判断出这个文章对自己是否有意义或者价值,是否需要进行更加详细的阅读;而提取关键词的好处是可以让文章与文章之间产生关联,同时也可以让读者通过关键词快速定位到和该关键词相关的文章内容。 文本摘要和关键词提取都可以和传统的 CMS 进行结合,通过对文章 / 新闻等发布功能进行改造,同步提取关键词和摘要,放到 HTML 页面中作为 Description 和 Keyworks。这样做在一定程度上有利于搜索引擎收录,属于 SEO 优化的范畴。 关键词提取 关键词提取的方法很多,但是最常见的应该就是tf-idf了。 通过jieba实现基于tf-idf关键词提取的方法: jieba.analyse.extract_tags(text, topK=5, withWeight=False, allowPOS=('n', 'vn', 'v')) 文本摘要 文本摘要的方法也有很多,如果从广义上来划分,包括提取式和生成式。其中提取式就是在文章中通过TextRank等算法,找出关键句然后进行拼装,形成摘...
- 下一篇
用 GitHub Action 构建一套 CI/CD 系统
缘起 Nebula Graph 最早的自动化测试是使用搭建在 Azure 上的 Jenkins,配合着 GitHub 的 Webhook 实现的,在用户提交 Pull Request 时,加个 ready-for-testing 的 label 再评论一句 Jenkins go就可以自动的运行相应的 UT 测试,效果如下: 因为是租用的 Azure 的云主机,加上 nebula 的编译要求的机器配置较高,而且任务的触发主要集中在白天。所以上述的方案性价比较低,从去年团队就在考虑寻找替代的方案,准备下线 Azure 上的测试机,并且还要能提供多环境的测试方案。 调研了一圈现有的产品主要有: TravisCI CircleCI Azure Pipeline Jenkins on k8s(自建) 虽然上面的产品对开源项目有些限制,但整体都还算比较友好。 鉴于之前 GitLab CI 的使用经验,体会到如果能跟 GitHub 深度集成那当然是首选。所谓“深度”表示可以共享 GitHub 的整个开源的生态以及完美的 API 调用(后话)。恰巧 2019,GitHub Action 2.0 横空出...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS关闭SELinux安全模块
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Hadoop3单机部署,实现最简伪集群