Python中的原地操作符函数与标准操作符函数有何不同?
Python中的原地操作符函数与标准操作符函数有何不同?
从docs中:
许多操作都有“原位”版本。以下函数提供了一种比常规语法更原始的访问原位运算符的方式;例如,语句 x += y 等同于 x = operator.iadd(x, y)。换句话说,z = operator.iadd(x, y) 等同于复合语句 z = x; z += y。
问题:
- 为什么 operator.iadd(x, y) 不等同于 z = x; z += y?
- operator.iadd(x, y) 与 operator.add(x, y) 有什么区别?
相关问题,但我对Python类方法不感兴趣;只对内置Python类型上的常规运算符感兴趣。
Python中的就地操作符函数与标准操作符函数有什么不同?
首先,你需要理解__add__
和__iadd__
之间的区别。
一个对象的__add__
方法是普通的加法:它接受两个参数,返回它们的和,不修改任何参数。
一个对象的__iadd__
方法也接受两个参数,但在原地进行修改,修改第一个参数的内容。因为这需要对象的变异,不可变类型(如标准数字类型)不应该有__iadd__
方法。
a + b
使用__add__
。a += b
使用__iadd__
(如果存在);如果不存在,它会通过__add__
来模拟,就像tmp = a + b; a = tmp
一样。operator.add
和operator.iadd
也是以相同的方式不同。
至于另一个问题:operator.iadd(x, y)
并不等同于z = x; z += y
,因为如果没有__iadd__
存在,则会使用__add__
。你需要将值赋给变量以确保结果在两种情况下都存储:x = operator.iadd(x, y)
。
你可以很容易地自己验证这一点:
import operator a = 1 operator.iadd(a, 2) # a仍然是1,因为整数没有__iadd__;iadd返回了3 b = ['a'] operator.iadd(b, ['b']) # 列表有__iadd__,所以b现在是['a', 'b']
docs.python.org/2/reference/datamodel.html#object.__iadd__ 请注意,增强赋值可以但不一定要就地修改对象。
为什么Python设计成增强赋值运算符在原地操作?这有点违反直觉,因为当我们学习语言时,通常会先学习具有不可变值的增强赋值。所以对于具有可变值的增强赋值行为不同,这有点出人意料并且容易出错。
我不知道你为什么声称“当我们学习语言时,通常会先学习具有不可变值的增强赋值”。字符串是不可变的,数值是可变的,列表是可变的,元组是不可变的。s = 'foo'
s += 'bar'
显然与x = 4.2
x += 2
或l = [1,5,6]
l += [2]
有不同。唯一令人困惑的地方是不知道LHS值的类型。
听起来像是设计学习材料的人的失误。增强赋值的目的是相当清晰的:PEP 203。
在Python中,有一些运算符函数可以在原地(in-place)修改对象,而标准的运算符函数则会创建一个新的对象并返回。这两种函数之间的区别在于它们对可变和不可变对象的处理方式不同。
这个问题的出现可能是因为一些Python对象是不可变的。我猜测operator.iadd(x, y)
对于可变类型(如字典和列表)等价于z = x; z += y
,但对于不可变类型(如数字和字符串)则不是这样。
解决方法是使用适当的运算符函数来处理不同类型的对象。对于可变对象,可以使用operator.iadd()
来原地修改对象。而对于不可变对象,则需要使用标准的运算符函数来创建一个新的对象并返回。
总结起来,Python的原地运算符函数与标准的运算符函数之间的区别在于它们对可变和不可变对象的处理方式不同。原地运算符函数可以在可变对象上直接进行修改,而标准的运算符函数则会创建一个新的对象并返回。正确选择适当的运算符函数可以确保对不同类型的对象进行正确的操作。