Python 中的运算符 += return 是什么意思
What does the operator += return in Python
原问题
当我试图在Whosebug上回答另一个人关于Python中=
和+=
之间的区别的问题时,我遇到了以下问题:
class Foo:
def __init__(self, value, name="default"):
self.value = value
self.name = name
def __add__(self, that):
return Foo(self.value + that.value)
def __iadd__(self, that):
self.value = self.value + that.value
return self
def __str__(self):
return "name: {}, value: {:d}".format(self.name, self.value)
a = Foo(1, 'alice')
b = Foo(2, 'bob')
print(a+=b)
上次 print
调用不成功,给了我这个:
File "<ipython-input-8-0faa82ba9e4a>", line 3
print(a+=b)
^
SyntaxError: invalid syntax
我不知道为什么这不起作用。也许它与关键字参数传递机制有关?我只是找不到关于这个主题的任何资源,因为重载的 __iadd__
方法已经 returns 一个 Foo
对象。
************** 更新 ******************
如果我像这样更改 __iadd__
方法(只需删除 return
语句):
...
def __iadd__(self, that):
print("__iadd__ was called")
self.value = self.value + that.value
a = Foo(1, 'alice')
b = Foo(2, 'bob')
a += b
print(a) # Outputs: None
因此,__iadd__
中最后的 return
语句确实是必需的。但它并没有像我想的那样起作用(我来自 C/C++ 背景,所以这种行为对我来说有点奇怪)
*************************** 第二次更新 ****************** **************
我差点忘了 Python 中的 =
构成了语句而不是表达式。 __iadd__
中的 return
语句和我使用其他语言的经验让我产生一种错觉,认为 +=
可以用作表达式。
如 Python 文档中所述,__add__
用于构造新对象。 __iadd__
专为就地修改而设计。但这一切都取决于实施。虽然 __iadd__
returns 是一个对象,但是这个对象不知何故被语言“截获”了,重新赋值给了左边的运算符,最后的效果是,__iadd__
仍然是一个语句,而不是一种表达。如果我错了,请纠正我。另外,我没有找到任何资源来确认 +=
是一个声明。
总结
- 一个明码,比如说
a = 1
是一个赋值语句。它不允许用作函数参数。但是,关键字参数传递不受此限制:print('Hello world', end='')
仍然有效。
-
x = x + y
等同于x = x.__add__(y)
,
x += y
等同于 x = x.__iadd__(y)
,查看文档了解更多详情。
- 一个例子:
class Foo:
def __init__(self, value, name="default"):
self.value = value
self.name = name
def __add__(self, that):
return Foo(self.value + that.value)
def __iadd__(self, that):
self.value = self.value + that.value
return self
def __str__(self):
return "name: {}, value: {:d}".format(self.name, self.value)
a = Foo(1, 'alice')
b = Foo(2, 'bob')
c = a + b # objects a and b are unchanged
a += b # object a is changed
print(c) # name: default, value: 3
print(a) # name: alice, value: 3
这是使用赋值语句作为表达式导致的语法错误。这是另一个 SO post 解释了 difference between statements and expressions.
这不是运算符重载实现的错误。
语法如何在另一个上下文中导致错误的示例(使用 int
而不是 Foo
):
a = 2
b = 3
# STILL AN ERROR!
print(a += b) # Error while using the standard int += operator
如何解决语法问题:
a = 2
b = 3
a += b # Separate the assignment statement...
print(a) # ...and give the print function an expression
+=
运算符是一组多个 扩充赋值运算符 的一部分。您可以将这些视为赋值语句的快捷方式,其中分配的变量也在分配给变量的值中。所以,i += 1
等同于 i = i + 1
.
因此,您遇到的问题是因为只能将 值(整数、浮点数、字符串等)或表达式传递给 print()
功能。 a += b
不是一个值(它是一个语句)也不是一个表达式(它不会 评估 为单个值),所以你不能将它传递给 print()
.
解决这个问题:
a = Foo(1, 'alice')
b = Foo(2, 'bob')
a += b
print(a)
Assignments/in-place 操作不能被视为可以传递的表达式,这是设计使然。
除其他外,Guido 希望避免臭名昭著的 C 错字
if (a=b) // user actually wanted to test a==b
但是 python 3.8 提供了一种新的赋值方法,称为 assignment expression,允许将赋值作为参数传递(这在列表理解中非常方便)。
你仍然不能进行就地添加,但你可以先添加然后赋值:
>> a=2
>> b=3
>> print(a:=a+b)
5
Python 的语义可能令人困惑。作为说明性示例,请尝试完成以下示例:
class Foo:
def __init__(self, value):
self.value = value
def __iadd__(self, other):
self.value = self.value + other.value
return 'did iadd'
a = Foo(1)
c = a
c += Foo(2)
print((c, a.value))
应该导致 ('did iadd', 3)
被打印
根据 docs:
x += y
is equivalent to x = x.__iadd__(y)
__iadd__
的约定是您应该以 x = x + y
的语义评估为与 x += y
“相同”的方式来实现它。 “相同”取决于问题的细节,例如对于不可变对象(例如整数或字符串),这将需要分配一个新对象并更新指向的引用,例如:
a = 500 # move out of the "interned" number space
c = a
a += 1
print(id(a), id(c))
应该给你两个不同的数字,在 CPython 中这将是两个 int 对象的地址,既不同又不可变。对于其他数据类型,允许 mutable/inplace 更新发生可能很有用,例如出于效率或其他原因。 __iadd__
的语义允许 class 实现选择默认值。
每种语言都有自己的特点,因此我建议不要试图将它与 C++ 进行过于字面的比较。特别是因为 C++ 有很多历史包袱,它被迫随身携带。
原问题
当我试图在Whosebug上回答另一个人关于Python中=
和+=
之间的区别的问题时,我遇到了以下问题:
class Foo:
def __init__(self, value, name="default"):
self.value = value
self.name = name
def __add__(self, that):
return Foo(self.value + that.value)
def __iadd__(self, that):
self.value = self.value + that.value
return self
def __str__(self):
return "name: {}, value: {:d}".format(self.name, self.value)
a = Foo(1, 'alice')
b = Foo(2, 'bob')
print(a+=b)
上次 print
调用不成功,给了我这个:
File "<ipython-input-8-0faa82ba9e4a>", line 3
print(a+=b)
^
SyntaxError: invalid syntax
我不知道为什么这不起作用。也许它与关键字参数传递机制有关?我只是找不到关于这个主题的任何资源,因为重载的 __iadd__
方法已经 returns 一个 Foo
对象。
************** 更新 ******************
如果我像这样更改 __iadd__
方法(只需删除 return
语句):
...
def __iadd__(self, that):
print("__iadd__ was called")
self.value = self.value + that.value
a = Foo(1, 'alice')
b = Foo(2, 'bob')
a += b
print(a) # Outputs: None
因此,__iadd__
中最后的 return
语句确实是必需的。但它并没有像我想的那样起作用(我来自 C/C++ 背景,所以这种行为对我来说有点奇怪)
*************************** 第二次更新 ****************** **************
我差点忘了 Python 中的 =
构成了语句而不是表达式。 __iadd__
中的 return
语句和我使用其他语言的经验让我产生一种错觉,认为 +=
可以用作表达式。
如 Python 文档中所述,__add__
用于构造新对象。 __iadd__
专为就地修改而设计。但这一切都取决于实施。虽然 __iadd__
returns 是一个对象,但是这个对象不知何故被语言“截获”了,重新赋值给了左边的运算符,最后的效果是,__iadd__
仍然是一个语句,而不是一种表达。如果我错了,请纠正我。另外,我没有找到任何资源来确认 +=
是一个声明。
总结
- 一个明码,比如说
a = 1
是一个赋值语句。它不允许用作函数参数。但是,关键字参数传递不受此限制:print('Hello world', end='')
仍然有效。 -
x = x + y
等同于x = x.__add__(y)
,x += y
等同于x = x.__iadd__(y)
,查看文档了解更多详情。
- 一个例子:
class Foo:
def __init__(self, value, name="default"):
self.value = value
self.name = name
def __add__(self, that):
return Foo(self.value + that.value)
def __iadd__(self, that):
self.value = self.value + that.value
return self
def __str__(self):
return "name: {}, value: {:d}".format(self.name, self.value)
a = Foo(1, 'alice')
b = Foo(2, 'bob')
c = a + b # objects a and b are unchanged
a += b # object a is changed
print(c) # name: default, value: 3
print(a) # name: alice, value: 3
这是使用赋值语句作为表达式导致的语法错误。这是另一个 SO post 解释了 difference between statements and expressions.
这不是运算符重载实现的错误。
语法如何在另一个上下文中导致错误的示例(使用 int
而不是 Foo
):
a = 2
b = 3
# STILL AN ERROR!
print(a += b) # Error while using the standard int += operator
如何解决语法问题:
a = 2
b = 3
a += b # Separate the assignment statement...
print(a) # ...and give the print function an expression
+=
运算符是一组多个 扩充赋值运算符 的一部分。您可以将这些视为赋值语句的快捷方式,其中分配的变量也在分配给变量的值中。所以,i += 1
等同于 i = i + 1
.
因此,您遇到的问题是因为只能将 值(整数、浮点数、字符串等)或表达式传递给 print()
功能。 a += b
不是一个值(它是一个语句)也不是一个表达式(它不会 评估 为单个值),所以你不能将它传递给 print()
.
解决这个问题:
a = Foo(1, 'alice')
b = Foo(2, 'bob')
a += b
print(a)
Assignments/in-place 操作不能被视为可以传递的表达式,这是设计使然。
除其他外,Guido 希望避免臭名昭著的 C 错字
if (a=b) // user actually wanted to test a==b
但是 python 3.8 提供了一种新的赋值方法,称为 assignment expression,允许将赋值作为参数传递(这在列表理解中非常方便)。
你仍然不能进行就地添加,但你可以先添加然后赋值:
>> a=2
>> b=3
>> print(a:=a+b)
5
Python 的语义可能令人困惑。作为说明性示例,请尝试完成以下示例:
class Foo:
def __init__(self, value):
self.value = value
def __iadd__(self, other):
self.value = self.value + other.value
return 'did iadd'
a = Foo(1)
c = a
c += Foo(2)
print((c, a.value))
应该导致 ('did iadd', 3)
被打印
根据 docs:
x += y
is equivalent tox = x.__iadd__(y)
__iadd__
的约定是您应该以 x = x + y
的语义评估为与 x += y
“相同”的方式来实现它。 “相同”取决于问题的细节,例如对于不可变对象(例如整数或字符串),这将需要分配一个新对象并更新指向的引用,例如:
a = 500 # move out of the "interned" number space
c = a
a += 1
print(id(a), id(c))
应该给你两个不同的数字,在 CPython 中这将是两个 int 对象的地址,既不同又不可变。对于其他数据类型,允许 mutable/inplace 更新发生可能很有用,例如出于效率或其他原因。 __iadd__
的语义允许 class 实现选择默认值。
每种语言都有自己的特点,因此我建议不要试图将它与 C++ 进行过于字面的比较。特别是因为 C++ 有很多历史包袱,它被迫随身携带。