【问题标题】:How do you return a changed Python numpy array size reference parameter如何返回更改后的 Python numpy 数组大小参考参数
【发布时间】:2019-05-22 20:19:16
【问题描述】:

这是一个 Python 函数参数传递问题。 我想要一个 Python 函数来调整作为函数引用参数之一的 numpy 数组的大小。

传递的数组的内容似乎在函数内部和外部发生了变化。不知何故,数组对象的更新大小/形状没有从函数中导出,即使我认为 Python 通过引用传递参数。我是 Python 编程的新手,并希望通过引用来更新对象的所有方面。我是否需要明确“导出”更改?

#!/opt/local/bin/python2.7

# Function Test returning changed array
import numpy

def adjust( a1, a2 ) :
  " Adjust passed arrays (my final function will choose which one to adjust from content) "
  print str(a1.shape) + " At start inside function"
  a1[-1,0] = 99
  a1 = numpy.delete(a1, -1, 0)
  print str(a1.shape) + " After delete inside function"
  return None


d1 = numpy.array( [ [ 1,  2,  3],
                    [11, 12, 13],
                    [21, 22, 23],
                    [31, 32, 33]  ] )
d2 = numpy.array( [ [ 9,  8,  7],
                    [19, 18, 17]  ] )

print str(d1.shape) + " At start"
# Let us delete the last row
d1 = numpy.delete(d1, -1, 0)
print str(d1.shape) + " After delete"
# Worked as expected

# So far so good, now do it by object reference parameters in a function......
adjust( d1, d2 )
print d1
print str(d1.shape) + " After function delete return"
# Reference fails to update object properties

不知何故,引用的数组对象没有更新它的大小/形状属性。返回的数组中应该只有 2 行。

(4, 3) At start
(3, 3) After delete
(3, 3) At start inside function
(2, 3) After delete inside function
[[ 1  2  3]
 [11 12 13]
 [99 22 23]]
(3, 3) After function delete return

所以主线/全局代码按预期工作,该函数无法调整大小,但末尾现在删除的行显示了更新的数据。 记住最终函数会选择几个参数中的哪一个来调整,我如何从函数中完全导出参数的更改形状/大小?

【问题讨论】:

    标签: python numpy function-parameter


    【解决方案1】:

    https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.delete.html

    返回:“删除了 obj 指定的元素的 arr 副本。请注意,删除不会就地发生。如果 axis 为 None,out 是扁平化数组。”

    这意味着delete 不会修改原始数组——它只是制作一个副本。将此与元素分配 a1[-1,0] = 99 进行对比,后者确实修改了数组。我不相信 numpy 出于性能原因允许动态数组调整大小。

    至于您的困惑,参数是通过引用传递的(因此a1 的数组最初与d1 的数组相同)。然而,a1 = numpy.delete(a1, -1, 0) 的赋值是重新绑定名称a1,而不是修改它指向的数组,所以d1 没有改变。

    如果这没有意义,您应该阅读更多关于变量如何在 Python https://mathieularose.com/python-variables/ 中工作的信息,但基本上,名称(eq a1d1)是字典键,而对名称的赋值是更改与该键关联的值,这不会影响与该值关联的任何其他键。

    【讨论】:

    • 感谢您的出色解释。我在理解正在发生的事情方面取得了一些进展,但是将这些事情视为“名称”会有所帮助。通过在分配后更改 a1,我可以看到 a1 现在指的是一个新对象。我无法制定从函数更改 d1 的语法。我想我的问题是:-“我如何将 d1 更新到新副本?”。我是否必须以某种方式传递对 d1 和 d2 名称的引用?或者我是否必须将 a1 和 d1 作为“numpy.delete(a1, -1, 0)”调用的一部分进行变异?但是我不确定所需的语法。
    • 就像np.delete(a1, ...) 返回一个新数组一样,您的adjust 函数可以返回a1 变量,然后您可以将其分配给您喜欢的任何变量。即使函数确实修改了参数,所以通过引用传递的 id 仍然有效,返回该对象也没有什么坏处。
    • 正如我所提到的,我是 Python 编程的新手,所以我不太理解您在这里建议的语法。请记住,在最终代码中,函数有 2 个数组参数对象,我的目标是让函数选择要修改的对象,并将其应用于适当的参数。
    • 最pythonic的方式可能是返回两个数组,改变的和不变的。您可以将 d1 和 d2 分配给输出 (return a1, a2 ... d1, d2 = adjust(d1, d2) )。
    【解决方案2】:

    另一种解决方案(比 numpy 调用效率低).....

    我确信我在搜索答案时遇到了一些“就地删除”示例,但我当时并不完全理解这可能是我所需要的。 诀窍是确保本地名称(形式参数)不会通过函数中的“=”重新分配给新引用,因此通过这种方式,所有更改都对与传递参数相同的引用(名称)进行(实际参数)到函数。

    鉴于名称在赋值 (=) 上的行为方式,Python 的引用调用可能更像是值指针调用。

    # Warning Unsafe - array parameter resized in place
    # Insure array is not in use on calling
    # Requires array to be in C order
    def inplace_row_delete( a, r, c=1 ) :
      " Dangerously and inefficiently delete a row in-place on a numpy array (no range checking) "
      # a  reference to the numpy array to have a row deleted from
      #    This routine takes care not to re-assign this in effect "local" name
      #    so as to change the called array in place.
      # r  row to delete in numpy array a
      # c  Nr of rows from r (inclusive) to delete (default 1)
    
      rows = a.shape[0]
      if (r < 0):
        # support -ve indexing back from end
        r = rows + r
      # Move all the elements after row r, forward one (or c) place(s)
      for index in range(r, rows - c):
        a[index] = a[index + c]
    
      # Now make the array smaller, disposing of the last (now repeated) element(s)
      # tuples are immutable, but we need to reduce the first element (for the rows in
      # the array) by 1 (c), but keep however many others there are, the same.
      sh    =  list(a.shape)
      sh[0] -= c
      sh    =  tuple(sh)
      # This numpy re-size happens inplace
      # REQUIRES C order
      a.resize(sh, refcheck=False)
      return None
    

    【讨论】:

    • 以这种方式使用numpy.ndarray.resize 是非常不安全的,在常见情况下会导致内存损坏或静默错误结果。例如,如果 a 是 Fortran 顺序而不是 C 顺序,resize 将保留错误的元素。如果a 的任何视图正在使用中,它们将查看已释放的内存,这可能会导致内存损坏。
    • 所以像tmp=numpy.delete(a); resize; numpy.copy(a,tmp) 这样的东西可能会更安全一些。我想至少应该修改这个答案@user2356112?非常感谢。
    • 我同意这个答案在目前的形式下没有帮助,因为 Fortran 不兼容。我想它应该因此被删除。
    猜你喜欢
    • 2012-06-28
    • 1970-01-01
    • 2015-07-31
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    • 1970-01-01
    相关资源
    最近更新 更多