Shi*_* Xu 13 python variable-assignment
当我试图在 stackoverflow 上回答另一个人关于Python=
和+=
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)
Run Code Online (Sandbox Code Playgroud)
最后一个print
电话没有成功,给了我这个:
File "<ipython-input-8-0faa82ba9e4a>", line 3
print(a+=b)
^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)
我不知道为什么这不起作用。也许它与关键字参数传递机制有关?我只是找不到关于这个主题的任何资源,因为重载的__iadd__
方法已经返回了一个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
Run Code Online (Sandbox Code Playgroud)
所以,return
in 中的最终语句__iadd__
确实是必需的。但它并没有像我想象的那样运行(我来自 C/C++ 背景,所以这种行为对我来说有点奇怪)
************************* 第二次更新 *********************** *********
我几乎忘记了=
在 Python 中是由语句而不是表达式组成的。中的return
语句__iadd__
和我使用其他语言的经验给了我一种+=
可以用作表达的错觉。
正如 Python 文档中所述,__add__
用于构造一个新对象。__iadd__
专为就地修改而设计。但这一切都取决于实现。虽然__iadd__
返回一个对象,但这个对象以某种方式被语言“拦截”并重新分配给左侧运算符,最终效果是,__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
Run Code Online (Sandbox Code Playgroud)
赋值/就地操作不能被视为可以传递的表达式,这是设计使然。
除此之外,Guido 还想避免臭名昭著的 C 错字
if (a=b) // user actually wanted to test a==b
Run Code Online (Sandbox Code Playgroud)
但是 python 3.8 提供了一种新的赋值方法,称为赋值表达式,它允许将赋值的值作为参数传递(这在列表推导中非常方便)。
您仍然无法进行就地添加,但您可以添加然后分配:
>> a=2
>> b=3
>> print(a:=a+b)
5
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)
应该导致('did iadd', 3)
被打印
根据文档:
x += y
相当于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))
Run Code Online (Sandbox Code Playgroud)
应该给你两个不同的数字,在 CPython 中这将是两个 int 对象的地址,既不同又不可变。对于其他数据类型,允许发生可变/就地更新可能很有用,例如出于效率或其他原因。__iadd__
允许类实现选择默认值的语义。
每种语言都有自己的特点,因此我建议不要试图将它与 C++ 进行过于字面的比较。特别是因为 C++ 有很多历史包袱,它被迫随身携带。
归档时间: |
|
查看次数: |
2518 次 |
最近记录: |