是按值而不是通过引用返回所有对象吗?

时间:2017-11-12 19:45:34

标签: python function numpy heap-memory

我在Python中编码试图决定是否应该返回一个numpy数组(某个其他数组上的diff的结果)或返回numpy.where(diff)[0],这是一个较小的数组但需要的很少额外的工作要创造。让我们调用methodB发生的方法。

我从methodA调用methodB。问题是我不一定总是需要methodA中的where()结果,但我可能会。因此值得在methodB中完成这项工作,还是应该传回(更大的内存方式)diff本身,然后只在需要时才在methodA中进一步处理它?假设methodA只获得对结果的引用,那将是更有效的选择。

那么,当函数结果传回给调用该函数的代码时,它们是否曾被复制过?

我相信当methodB完成时,系统会回收其帧中的所有内存,因此methodA必须实际复制将methodB返回的任何内容放入其自己的帧中才能够使用它。我会称之为“按价值回报”。这是对的吗?

4 个答案:

答案 0 :(得分:2)

分配永远不会复制数据。如果您有一个返回值的函数foo,那么像result = foo(arg)这样的赋值永远不会复制任何数据。 (当然,您可以在函数体中进行复制操作。)同样,return x不会复制对象x

你的问题缺乏一个具体的例子,所以我不能详细说明。

编辑:你应该看一下优秀的Facts and Myths about Python names and values谈话。

答案 1 :(得分:2)

是的,你是对的。在Python中,参数始终按值传递,返回值始终按值返回。但是,返回(或传递)的值是对可能共享的,可能是可变对象的引用。

有些类型的返回或传递的值可能是实际的对象本身,例如对于整数来说就是这种情况,但两者之间的差异只能在整数不可变的对象中观察到,并且取消引用对象引用是完全透明的,所以你永远不会注意到差异。为了简化你的心智模型,你可以假设参数和返回值总是按值传递(无论如何都是这样),并且传递的值总是一个引用(这并不总是正确的,但是你无法区分它们,您可以将其视为简单的性能优化。)

请注意,按值传递/返回引用与通过引用传递/返回绝不相似(当然也不一样)。特别是,它允许你改变调用者/被调用者中的名称绑定,因为传递引用将允许你。

这种特殊的传值值,其值通常是参考值,例如在ECMAScript,Ruby,Smalltalk和Java,有时称为"通过对象共享调用" (由Barbara Liskov创造,我相信),"通过分享","通过对象"进行调用,特别是在Python社区内进行调用"通过分配调用" (感谢@timgeb)或"通过名称绑定调用" (感谢@Terry Jan Reedy)(不要与按名称调用混淆,这又是另一回事。)

答案 2 :(得分:0)

大致你的代码是:

def methodA(arr):
     x = methodB(arr)
     ....
def methodB(arr):
     diff = somefn(arr)
     # return diff   or
     # return np.where(diff)[0]

arr是一个(大)数组,传递给methodAmethodB的引用。没有制作副本。

diff是一个在methodB中生成的类似大小的数组。如果返回,则由methodAx命名空间中引用。返回时不会复制。

如果返回where数组,diff会在methodB返回时消失。假设它没有与其他数组(例如arr)共享数据缓冲区,则它所占用的所有内存都将被恢复。

但只要记忆力不紧张,返回diff代替where结果就不会更贵。在返回期间没有任何内容被复制。

numpy数组由小对象包装器组成,其中包含shapedtype等属性。它还有一个指向可能很大的data buffer的指针。尽可能numpy尝试共享缓冲区,但很容易生成新的ndarray个对象。因此,viewcopy之间存在重要区别。

答案 3 :(得分:0)

我看到我现在错过的内容:对象是在上创建的,但功能框位于堆栈上。因此,当methodB完成时,它的框架将被回收,但该对象仍将存在于堆上,而methodA可以通过简单的引用访问它。