【问题标题】:Why does `/=` raise an error but not `x = x / y` with a read-only numpy array?为什么 `/=` 会引发错误而不是 `x = x / y` 与只读 numpy 数组?
【发布时间】:2020-12-29 02:11:28
【问题描述】:

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

代码是这样的。 (是 Tensorflow 中的一个简单的深度学习代码,阅读代码 cmets 了解一些细节)。

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

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

【问题讨论】:

  • 什么错误?请堆栈跟踪。一般来说,错误信息是不言自明的。
  • /= 正在尝试进行 in-place 更改,该更改仅适用于可变值。 x_train = x_train / 1 正在将一个对 不同 不可变值的引用放入变量中。所以正如疯狂物理学家所说——错误信息本身可以很好地告诉你到底是什么问题。
  • 为什么会有numpy标签?你不使用 tf 吗?
  • @MadPhysicist tf 使用 NumPy 并且 x_train 是一个 NumPy 数组。它写在代码的注释中。
  • 有趣的事实:在 x=x/1 的结果上调用 x/=1 可能会正常工作。

标签: python numpy


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    按照惯例,__add__ 从不改变调用它的对象。 __iadd__ 有时会,有时不会。在 Python 的内置函数中有这两个例子:

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

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

    开发人员可以选择只读数组:要么更改__iadd__ 的语义,要么引发错误。最不意外原则决定了后者:一个函数在某些情况下不应该在不告诉你的情况下改变它的基本行为。假设您使用__iadd__ 而不是__add__ 是安全的,因为您想要就地操作。该功能会在无法执行此操作时通知您,因为替代方法是执行您未明确要求的操作。

    【讨论】:

      猜你喜欢
      • 2010-11-05
      • 2019-05-13
      • 1970-01-01
      • 1970-01-01
      • 2021-06-07
      • 1970-01-01
      • 2012-08-08
      • 2014-05-09
      • 2012-10-19
      相关资源
      最近更新 更多