最近我在做it技术方面的工作时,越来越多地使用python代替c/c++,因为用它可以将我的编码时间缩短几倍。但是当我处理大量数据时,我的python程序运行速度开始变得比c慢很多。我想知道这是否是因为我没有有效地使用大对象/数组。是否有一份全面的指南,介绍numpy/python如何处理内存?什么情况下是按引用传递,什么情况下是按值传递,什么类型是可变的,什么类型是不可变的,以及什么情况下会进行复制操作等信息。
最近我在做it技术方面的工作时,越来越多地使用python代替c/c++,因为用它可以将我的编码时间缩短几倍。但是当我处理大量数据时,我的python程序运行速度开始变得比c慢很多。我想知道这是否是因为我没有有效地使用大对象/数组。是否有一份全面的指南,介绍numpy/python如何处理内存?什么情况下是按引用传递,什么情况下是按值传递,什么类型是可变的,什么类型是不可变的,以及什么情况下会进行复制操作等信息。
在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
请小心,因为该方法有可能返回错误的结果(尽管我从未看到过)。
在2013年的SciPy会议上,有一个关于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
相比如何。 - kon psych所以我将引用EOL的话,因为我认为他的回答非常相关:
3)最后一点与问题标题有关:“按值传递”和“按引用传递”不是Python中相关的概念。相反,相关的概念是“可变对象”和“不可变对象”。列表是可变的,而数字则不是,这解释了您观察到的情况。此外,您的Person1和bar1对象是可变的(这就是为什么可以更改人的年龄)。您可以在文本教程和视频教程中找到有关这些概念的更多信息。维基百科也提供了一些(更技术性的)信息。一个示例说明了可变和不可变之间行为差异的区别- EOL的回答。
总的来说,我发现Numpy/Scipy遵循这些规则;更重要的是,它们在文档中明确告诉您正在发生什么。
例如,np.random.shuffle
要求输入一个数组并返回 None
,而 np.random.permutation
返回一个数组。在这里,您可以清楚地看到哪个返回值,哪个不返回值。Numpy/Scipy
非常高效。pass-by-reference
更快,他们就会使用它。只要按照文档中的方式使用函数,您不应该在速度方面遇到重大问题。