我在Python中编码试图决定是否应该返回一个numpy数组(某个其他数组上的diff的结果)或返回numpy.where(diff)[0],这是一个较小的数组但需要的很少额外的工作要创造。让我们调用methodB发生的方法。
我从methodA调用methodB。问题是我不一定总是需要methodA中的where()结果,但我可能会。因此值得在methodB中完成这项工作,还是应该传回(更大的内存方式)diff本身,然后只在需要时才在methodA中进一步处理它?假设methodA只获得对结果的引用,那将是更有效的选择。
那么,当函数结果传回给调用该函数的代码时,它们是否曾被复制过?
我相信当methodB完成时,系统会回收其帧中的所有内存,因此methodA必须实际复制将methodB返回的任何内容放入其自己的帧中才能够使用它。我会称之为“按价值回报”。这是对的吗?
答案 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
是一个(大)数组,传递给methodA
和methodB
的引用。没有制作副本。
diff
是一个在methodB
中生成的类似大小的数组。如果返回,则由methodA
在x
命名空间中引用。返回时不会复制。
如果返回where
数组,diff
会在methodB
返回时消失。假设它没有与其他数组(例如arr
)共享数据缓冲区,则它所占用的所有内存都将被恢复。
但只要记忆力不紧张,返回diff
代替where
结果就不会更贵。在返回期间没有任何内容被复制。
numpy数组由小对象包装器组成,其中包含shape
和dtype
等属性。它还有一个指向可能很大的data buffer
的指针。尽可能numpy
尝试共享缓冲区,但很容易生成新的ndarray
个对象。因此,view
和copy
之间存在重要区别。
答案 3 :(得分:0)
我看到我现在错过的内容:对象是在堆上创建的,但功能框位于堆栈上。因此,当methodB完成时,它的框架将被回收,但该对象仍将存在于堆上,而methodA可以通过简单的引用访问它。