python numpy 和内存效率(通过引用与值传递)
Posted
技术标签:
【中文标题】python numpy 和内存效率(通过引用与值传递)【英文标题】:python numpy and memory efficiency (pass by reference vs. value) 【发布时间】:2013-07-27 01:12:27 【问题描述】:我最近越来越多地使用 python 来代替 c/c++,因为它可以将我的编码时间缩短几倍。同时,当我处理大量数据时,我的 python 程序的运行速度开始变得比 c 慢很多。我想知道这是否是因为我使用大对象/数组效率低下。 是否有任何关于 numpy/python 如何处理内存的综合指南?什么时候通过引用传递,什么时候通过值传递,什么时候复制,什么时候不复制,哪些类型是可变的,哪些不是。
【问题讨论】:
“Factor of a few”是我与非技术人员谈论我们为什么要改用 Python 的新技术术语。 This post 有大量与此问题相关的数据... @BlackVegetable 正确。无视 jdero 说的话。虽然原语(并且仅原语)是真正按值传递的,但它实际上是不可检测的,您可以将其视为一种优化。 假设你不知道某事是否是可变的,但你以某种方式弄明白了。你打算怎么变异?如果您通过确定它具有可以对其进行变异的特定接口来发现它是可变的,那么这并不是真正询问可变性 - 这是一种鸭式类型检查。如果您只知道它是可变的,那么您仍然不知道如何处理它。 @zhermes 好的,但这是您在学习如何使用相应类型时学到的东西。预先列出的清单不会有任何好处。如果您不知道如何对其进行变异,或者不知道如何对它进行任何操作,那么知道 X 是可变的对您没有任何好处。 【参考方案1】:python(和大多数主流语言)中的对象作为引用传递。
如果我们以 numpy 为例,通过索引现有数组创建的“新”数组只是原始数组的视图。例如:
import numpy as np
>>> vec_1 = np.array([range(10)])
>>> vec_1
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> vec_2 = vec_1[3:] # let vec_2 be vec_1 from the third element untill the end
>>> vec_2
array([3, 4, 5, 6, 7, 8, 9])
>>> vec_2[3] = 10000
array([3, 4, 5, 10000, 7, 8, 9])
>>> vec_1
array([0, 1, 2, 3, 4, 5, 10000, 7, 8, 9])
Numpy 有一个方便的方法来帮助您解决问题,称为 may_share_memory(obj1, obj2)。所以:
>>> np.may_share_memory(vec_1, vec_2)
True
请小心,因为该方法可能会返回误报(尽管我从未见过)。
在 SciPy 2013 上有一个关于 numpy 的教程 (http://conference.scipy.org/scipy2013/tutorial_detail.php?id=100)。最后,这个家伙谈到了 numpy 如何处理内存。观看。
根据经验,默认情况下对象几乎从不作为值传递。甚至那些封装在另一个对象上的。另一个例子,列表进行巡回演出:
Class SomeClass():
def __init__(a_list):
self.inside_list = a_list
def get_list(self):
return self.inside_list
>>> original_list = range(5)
>>> original_list
[0,1,2,3,4]
>>> my_object = SomeClass(original_list)
>>> output_list = my_object.get_list()
>>> output_list
[0,1,2,3,4]
>>> output_list[4] = 10000
>>> output_list
[0,1,2,3,10000]
>>> my_object.original_list
[0,1,2,3,10000]
>>> original_list
[0,1,2,3,10000]
令人毛骨悚然,是吗? 使用赋值符号 ("="),或在函数末尾返回一个,您将始终创建指向对象或其一部分的指针。 对象仅在您明确复制时才会复制,使用诸如 some_dict.copy 或 array[:] 之类的复制方法。例如:
>>> original_list = range(5)
>>> original_list
[0,1,2,3,4]
>>> my_object = SomeClass(original_list[:])
>>> output_list = my_object.get_list()
>>> output_list
[0,1,2,3,4]
>>> output_list[4] = 10000
>>> output_list
[0,1,2,3,10000]
>>> my_object.original_list
[0,1,2,3,10000]
>>> original_list
[0,1,2,3,4]
知道了吗?
【讨论】:
我认为在你的最后一个例子中my_object.original_list
应该是my_object.get_list()
。此外,当您在第一个示例中为 vec2[:]
分配值时,您可能希望添加与 vec2
相比的行为方式
我认为 [:] 只是为列表复制。如果 original_list 是数组,则不会被复制。【参考方案2】:
所以我将不得不在此引用 EOL,因为我认为他的回答非常相关:
3)最后一点和题名有关:“按值传递” 和“通过引用传递”不是相关的概念 Python。相关概念是“可变对象”和 “不可变对象”。列表是可变的,而数字不是,这 解释你观察到的。此外,您的 Person1 和 bar1 对象是 可变的(这就是您可以更改此人年龄的原因)。你可以找到 有关这些概念的更多信息,请参阅文本教程和视频 教程。***也有一些(更技术性的)信息。一个 示例说明了 mutable 和 不可变 - answer by EOL
总的来说,我发现 Numpy/Scipy 遵循这些;更重要的是,他们在文档中明确告诉您正在发生的事情。
例如
np.random.shuffle
请求输入数组并返回 None
而 np.random.permutation
返回一个数组。您可以在这里清楚地看到哪个返回值而不是返回值。
类似的数组具有传递引用语义,一般来说,我发现Numpy/Scipy
非常有效。
我认为公平地说,如果使用pass-by-reference
更快,他们会的。只要您按照文档所说的方式使用这些功能,就速度方面应该不会有重大问题。
您要询问的具体类型有哪些?
【讨论】:
感谢您的回答。不,我没有考虑过具体的类型。我更多的是寻找关于一般的、最佳的编码风格的答案,以提高计算效率。我认为这对于 python 可能不存在,除了相信 numpy/scipy 方法已经优化。 抱歉,一般来说 python 是为了方便而不是速度:)。但是,您可以在 C 中编写您想要“快速”的部分,并在 Python 中调用它们以实现快速运行时,或者如您所说,总是有 numpy/scipy。此外,为您的特定构建编译 numpy/scipy 有助于进一步优化它们!以上是关于python numpy 和内存效率(通过引用与值传递)的主要内容,如果未能解决你的问题,请参考以下文章