欢迎来到魔豆IT网-IT综合知识分析平台

详细说明增强算术赋值“-=”的操作

2020-09-14 01:11:45栏目 : 网络编程围观 : 11次

相关学习推荐:python教程

前言本文是Python语法糖系列文章之一。最新的源代码可以在desugar项目中找到(github.com/brettcannon…

简介Python有个东西叫增强型算术赋值。可能你不熟悉这个术语,但其实就是在做数学运算的同时赋值。比如a -= b是减法的增强算术赋值。

python版本中添加了增强的赋值。(翻译:在PEP-203中引入)

解析-=因为Python不允许覆盖赋值,所以和其他使用特殊/魔法方法的操作相比,它实现增强赋值的方式可能和你想象的不完全一样。

首先要知道a -= b和a = a-b在语义上是一样的,但是要注意,如果事先知道要给一个对象赋一个变量名,可能比a-b的盲目操作效率更高。

例如,最小的好处是可以避免创建新对象:如果可以原地修改对象,那么返回self比重建新对象更高效。

所以Python提供了__isub__()方法。如果它被定义在赋值操作的左边(通常称为左值),右边的值(通常称为右值)将被调用。因此,对于a -= b,我们将尝试调用a.__isub__(b)。

如果调用的结果不是Implemented,或者根本没有结果,Python会退回到常规的二进制算术运算:A-B(翻译:作者关于二进制运算的文章,翻译在这里)

最后,不管用哪种方法,返回值都会赋给一个。

下面是一个简单的伪代码,a -= b分解成:

#实现a -= b伪代码如果有(a," _ _ isub _ _ & quot):_ value = a. _ _ isub _ _ (b) if _ value未实现:a = _ value else:a = a-b del _ value else:a = a-b复制代码归纳这些方法并不太复杂,因为我们已经实现了二进制算术运算。

通过引入二进制算术运算函数并做一些自省(以及处理可能出现的TypeError),可以很漂亮地概括为:

def _ create _ binary _ in place _ op(binary _ op:_ BinaryOp)-& gt;可调用[[Any,Any],Any]:binary _ operation _ name = binary _ op . _ _ name _ _[2:-2]method _ name = f & quot;_ _ I { binary _ operation _ name } _ _ & quot;operator = f & quot{ binary _ op. _ operator } = & quotdef binary_inplace_op(左值:Any,右值:Any,/) ->any:lvalue _ type = type(lvalue)try:method = debuiltins。_mro_getattr(lvalue_type,method_name)除了AttributeError:pass else:value = method(lvalue,rvalue)如果值不是NotImplemented:返回值try: return binary_op(lvalue,rvalue)除了TypeError作为exc: #如果TypeError是由二进制算术运算符引起的,则抑制#它,以便我们可以为自动赋值提出适当的值。if exc。_binary_op!= binary _ op . _ operator:raise raise TypeError(f & quot;{operator}不支持的操作数类型:{lvalue_type!r}和{type(rvalue)!r } & quot)binary _ in place _ op . _ _ name _ _ _ = binary _ in place _ op . _ _ qual name _ _ _ = method _ name binary _ in place _ op . _ _ doc _ _ =(f & quot;""实现扩充算术赋值“a {operator} b”。""")return binary_inplace_op复制代码,使得定义的-= support _ create _ binary _ in place _ op(_ sub _ _)可以推断其他内容:函数名、调用什么__i*__函数、二进制算术运算出错时调用哪个可调用对象。

当我发现几乎没有人用* * =写这篇文章的代码时,我遇到了一个奇怪的测试错误* * =。在所有确保__pow__将被正确调用的测试中,Python标准库中的运算符模块有一个测试用例失败。

我的代码一般没问题。如果代码和CPython的代码有区别,通常说明我出了问题。

然而,无论我多么仔细地检查代码,我都无法理解为什么我的测试通过了,而标准库却失败了。

我决定更多地了解CPython内部发生的事情。从分解字节码开始:

>。>。>。def test(): a **= b...>。>。>。导入dis>。>。>。dis . dis(test)10 LOAD _ FAST 0(a)2 LOAD _ GLobaL 0(b)4 INPLACE_POWER 6 store _ FAST 0(a)8 LOAD _ const 0(none)10 return _ value通过它复制代码,我在评估循环中找到了in place _ power:

case TARGET(in place _ POWER):{ pyoObject * exp = POP();pyoObject * base = TOP();PyObject * RES = PyNumber _ inPlacePower(base,exp,Py _ None);Py _ DECREF(基数);py _ DERUF(exp);SET _ TOP(RES);如果(res == NULL)转到错误;DISPATCH();}复制代码来源:github.com/python/cpyt…

然后找到PyNumber_InPlacePower():

pyObject * PyNumber _ in PlacePower(pyObject * v,PyObject *w,pyObject * z){ if(v->;ob_type->tp _ as _ number & amp& ampv->;ob_type->tp_as_number->。nb_inplace_power!= NULL) {返回三元_op(v,w,z,NB_SLOT(nb_inplace_power),& quot* * = & quot);} else { return trial _ op(v,w,z,NB_SLOT(nb_power),& quot* * = & quot);}}复制代码来源:github.com/python/cpyt…

真是如释重负~代码显示如果定义了__ipow__就会调用,但是__pow__只有在__ipow__不可用的情况下才会调用。

但是正确的方法是调用__pow__和__rpow__如果调用__ipow__时出现问题,并且返回NotImplemented或者根本没有返回。

换句话说,当__ipow__存在时,上面的代码意外地跳过了a**b的备份语义!

事实上,大约11个月前,这个问题被部分发现,并提交了一个bug。我修复了这个问题,并在python-dev上进行了解释。

到目前为止,Python 3.10中似乎已经修复了这个问题,我们需要添加一个通知* * = 3.8和3.9的文档中有一个bug(这个问题可能已经存在很长时间了,但是旧版Python已经只处于安全维护模式,所以文档不会改变)。

修复后的代码很可能不会被移植,因为是语义变化,很难判断是否有人偶然依赖了有问题的语义。但是这个问题过了很久才被注意到,说明* * =没有被广泛使用,否则问题早就被发现了。

想了解更多的编程学习,来

展开剩余内容

分享到:

猜你喜欢

  • 下载1.3.1安装版的守护神云备份系统

    守护神云备份系统是专门针对百度网盘和金山快盘推出的资源存储备份工具。该工具支持这两个网络磁盘的实时和常规数据存储。功能描述自动将磁盘上的文件备份到金山快盘或百度网盘,软件完全免...

    2020-09-20
  • Excel汇总专家下载共享版6.0.1

    快速批量处理多个Excel文件,自动完成各种汇总任务,解决跨Excel文件汇总的问题。大大提高你的工作效率。汇总效率极高。以一个65536行20列的报表为例,总结一个报表只需要...

    2020-09-20
  • 藏文转换大师下载2016绿色版

    藏文翻译大师是一款多码藏汉双语界面的藏藏文翻译软件。藏文翻译大师支持不同编码类型的藏文文件翻译,如喜玛拉雅、桑巴他、潘地塔、童渊、方正、华光等多种相同编码字体,解决了不同编码藏...

    2020-09-20
  • 微压VeryZIP下载1.0.1.8官方版

    Micro-pressure是一款完全免费的解压压缩软件,不再担心传统压缩软件的共享版、40天试用期、购买许可证、破解版、修订版,完美支持包括win8、win7、vista、w...

    2020-09-20
热门标签