为什么`/ =`使用只读numpy数组会引发错误,而不会引发x = x / y错误呢?

时间:2020-09-10 14:38:35

标签: python numpy

我一直认为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

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

2 个答案:

答案 0 :(得分:6)

Immutable numpy array?中所述,可以将numpy数组显式标记为只读

运行somearray =/ value时,您要求以某种方式(对于典型的可变对象,例如 most numpy数组)更改该数组,而不仅仅是单个引用,但对象本身;这意味着somearray所有副本(包括提供它的tensorflow库内部的副本)都可能发生更改。

相比之下,运行somearray = somearray / value时,您正在创建一个 new 对象,而不修改旧对象,因此与somearray被标记为已读-

使用中的__itruediv__的实现可以 返回一个全新的对象,而不是修改只读数组,从而使=/在只读数组上的工作原理相同+=适用于整数的方式;但是,这意味着让操作分配内存-可能会很昂贵;对于numpy来说,除非作者知道自己已经做完了,否则不要做昂贵的事情,这样一来,人们就不会因不必要的错误而编写缓慢的代码。 (具有只读标志会显着更改对象的性能和内存使用特性,这是开发人员需要保持头脑中的正确状态才能编写正确代码的相当多的额外状态!)

答案 1 :(得分:3)

这与python运算符的语义和最少惊讶的原理有关。

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

按照惯例,__add__永远不会使调用它的对象发生突变。 __iadd__有时会,有时不会。在Python的内置函数中都有这两种示例:

  • intstr是不可变的,因此请始终返回一个新对象。在这种情况下,重新分配步骤非常重要,因为否则名称将不会绑定到新值。
  • list就地添加。在这种情况下,重新分配实际上是无人值守的(但仍然会发生)。

Numpy数组通常是可变的。像+=-=*=/=之类的操作是真正就位的。实际上,它们使用add参数由支持常规操作的相同ufunc(subtractmultiplytrue_divideout)支持。

开发人员可以选择创建只读数组:更改__iadd__的语义,或者引发错误。最少惊奇的原则决定了后者:在不通知您的情况下,函数在某些情况下不应更改其基本行为。可以安全地假设您使用__iadd__而不是__add__,因为您需要就地操作。该功能会在无法执行此操作时通知您,因为替代方法是执行您明确不要求的操作。