为什么"/="会引发错误,而只读numpy数组不会引发"x=x/y"



我一直认为x /= y等于x = x / y。但现在我面临的情况是,当我使用/=时会出现错误,但在使用x = x / y时不会出现错误。所以在python中它们肯定不应该是一样的。

代码是这样的。(是Tensorflow中的一个简单的深度学习代码,请阅读代码注释以了解一些详细信息(。

import tensorflow as tf
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()
# x_train is a (60000, 28, 28) numpy matrix
x_train /= 1 # this will raise error "ValueError: output array is read-only"
x_train = x_train / 1 # but this will work fine
ValueError                                Traceback (most recent call last)
<ipython-input-44-fceb080f135a> in <module>()
1 
----> 2 x_train /= 1
ValueError: output array is read-only

我想问一下它们之间的区别。为什么我从/=得到这个错误?

如Immutable numpy数组中给出的?,numpy数组可以显式标记为只读

当您运行somearray /= value时,您要求修改该数组,这样(对于典型的可变对象,如大多数numpy数组(不仅会更改单个引用,还会更改对象本身;这意味着somearray的所有副本(包括提供它的tensorflow库内部的副本(都会发生更改。

相比之下,当您运行somearray = somearray / value时,您正在创建一个对象,而不是修改旧对象,因此somearray标记为只读不会有冲突。

__itruediv__中的实现可以返回一个全新的对象,而不是修改只读数组,从而使只读数组上的/=的工作方式与+=对整数的工作方式相同;然而,这意味着要让操作分配内存——这可能很昂贵;对于numpy来说,除非作者知道正在做昂贵的事情,否则不要做这些事情是有意义的,这样人们就不会错误地编写不必要的慢代码。(让只读标志显著改变对象的性能和内存使用特性将是一种相当多的额外状态,开发人员需要将其记在脑子里才能编写正确的代码!(

这与python运算符语义和最小惊喜原则有关。

  • 表达式x = x + 1近似等价于x = type(x).__add__(x, 1)
  • 表达式x += 1近似等于x = type(x).__iadd__(x, 1)

按照惯例,__add__从不更改它调用的对象。__iadd__有时会更改,有时不会。Python的内置程序中有两个例子:

  • intstr是不可变的,所以总是返回一个新对象。在这种情况下,重新分配步骤非常重要,因为否则名称将不会绑定到新值
  • list添加到位。在这种情况下,重新分配实际上是不可行的(但它仍然会发生(

Numpy数组通常是可变的。像+=-=*=/=等操作确实到位。事实上,使用out参数,它们由支持常规操作(addsubtractmultiplytrue_divide(的相同ufunc支持。

开发人员可以选择只读数组:要么更改__iadd__的语义,要么引发错误。最小惊奇原则决定了后者:函数在某些情况下不应该在没有告诉你的情况下改变其基本行为。可以放心地假设您使用了__iadd__而不是__add__,因为您想要一个就地操作。当函数不能做到这一点时,它会通知你,因为另一种选择是做一些你没有特别要求的事情。

最新更新