deepcopy和python-避免使用它的提示?
我有一个非常简单的python例程,它涉及循环遍历大约20000个纬度和经度坐标的列表,并计算每个点到参考点的距离deepcopy和python-避免使用它的提示?,python,deep-copy,Python,Deep Copy,我有一个非常简单的python例程,它涉及循环遍历大约20000个纬度和经度坐标的列表,并计算每个点到参考点的距离 def compute_nearest_points( lat, lon, nPoints=5 ): """Find the nearest N points, given the input coordinates.""" points = session.query(PointIndex).all() oldNearest = [] newNe
def compute_nearest_points( lat, lon, nPoints=5 ):
"""Find the nearest N points, given the input coordinates."""
points = session.query(PointIndex).all()
oldNearest = []
newNearest = []
for n in xrange(nPoints):
oldNearest.append(PointDistance(None,None,None,99999.0,99999.0))
newNearest.append(obj2)
#This is almost certainly an inappropriate use of deepcopy
# but how SHOULD I be doing this?!?!
for point in points:
distance = compute_spherical_law_of_cosines( lat, lon, point.avg_lat, point.avg_lon )
k = 0
for p in oldNearest:
if distance < p.distance:
newNearest[k] = PointDistance(
point.point, point.kana, point.english, point.avg_lat, point.avg_lon, distance=distance
)
break
else:
newNearest[k] = deepcopy(oldNearest[k])
k += 1
for j in range(k,nPoints-1):
newNearest[j+1] = deepcopy(oldNearest[j])
oldNearest = deepcopy(newNearest)
#We're done, now print the result
for point in oldNearest:
print point.station, point.english, point.distance
return
所以基本上我只是制作一个输入的完整副本,并附加一个新的值-到参考点的距离。然后我将“sorted”应用于结果列表,指定排序键应该是PointDistance对象的distance属性
这比使用deepcopy快得多,尽管我承认我真的不明白为什么。我想这取决于高效的C实现python的“排序”?好的,首先是最简单的事情:
deepcopy
通常速度很慢,因为它必须做大量的内部簿记,才能以一种正常的方式复制病理病例,比如包含自身的对象。例如,请参阅或查看Python路径中的copy.py
中的deepcopy
的源代码sorted
很快,因为它是用C实现的。比Python中的等效排序快得多a=1
时,想想它有1
作为一个单独存在的对象,而a
只是一个附加到它的标签。在其他一些语言(如C)中,变量是容器(而不是标记),当您执行a=1
时,实际上将1放入a
。对于Python,变量是引用,这一点不适用。这有一些有趣的结果,您可能也会偶然发现:
>>> a = [] # construct a new list, attach a tag named "a" to it
>>> b = a # attach a tag named "b" to the object which is tagged by "a"
>>> a.append(1) # append 1 to the list tagged by "a"
>>> print b # print the list tagged by "b"
[1]
之所以会看到这种行为,是因为列表是可变对象:您可以在创建列表后对其进行修改,并且在通过引用列表的任何变量访问列表时会看到修改。列表的不变等价物是元组:
>>> a = () # construct a new tuple, attach a tag named "a" to it
>>> b = a # now "b" refers to the same empty tuple as "a"
>>> a += (1, 2) # appending some elements to the tuple
>>> print b
()
这里,a+=(1,2)
从a
引用的现有元组中创建一个新元组,再加上另一个动态构造的元组(1,2)
,并且a
被调整为指向新元组,当然b
仍然引用旧元组。类似于a=a+2
这样的简单数字加法也会发生同样的情况:在这种情况下,a
最初指向的数字不会以任何方式发生变异,Python“构造”一个新数字并移动a
指向新数字。简言之,数字、字符串和元组是不可变的;列表、目录和集合是可变的。用户定义的类通常是可变的,除非您明确地确保内部状态不能被改变。还有一个frozenset
,它是一个不可变的集合。当然还有很多其他的:)
我不知道您的原始代码为什么不起作用,但您可能遇到了与我在列表中显示的代码片段相关的行为,因为默认情况下,PointDistance
类也是可变的。另一种选择是collections
中的namedtuple
类,它构造了一个类似元组的对象,其字段也可以通过名称访问。例如,您可以这样做:
from collections import namedtuple
PointDistance = namedtuple("PointDistance", "point distance")
这将为您创建一个PointDistance
类,该类有两个命名字段:point
和distance
。在主for
循环中,可以适当地分配这些参数。由于在for
循环过程中,点
字段指向的点对象不会被修改,并且距离
是一个数字(根据定义,它是不可变的),因此这样做应该是安全的。但是是的,一般来说,简单地使用sorted
似乎更快,因为sorted
是用C实现的。您还可以使用heapq
模块,它实现了一个由普通Python列表支持的堆数据结构,因此,它可以让您轻松地找到顶部的k
元素,而无需对其他元素进行排序。然而,由于heapq
也是在Python中实现的,除非你有很多要点,否则sorted
的效果会更好
最后,我想补充一点,到目前为止,我从未使用过
deepcopy
,所以我想在大多数情况下都有办法避免它。我知道这并不能直接解决您的问题(我知道这是一个老问题),但是,由于有一些关于性能的讨论,因此有必要研究一下append
操作。您可能需要考虑“预分配”数组。例如:
array = [None] * num_elements
for i in range(num_elements):
array[i] = True
与:
array = []
for i in range(num_elements):
array.append(True)
这两种方法的一个简单的
timeit
运行表明,如果您为num\u元素的中等值预先分配数组,那么PointDistance
类的效果如何?如果将PointDistance
类设置为一个简单类,只引用原始点及其距离(即它实际上是一个包含两个元素的元组),您不需要使用deepcopy
,因为点在算法过程中不会改变,距离是一个简单的数字。@Tamás是的,它基本上只是一个字典。但是,在第一个示例中,如果没有deepcopy,这肯定不起作用。如果我干脆把这门课全部取消,改用字典的话,也许会有用吧?老实说,我只是对参考模型没有足够清晰的理解,不知道在这些情况下会发生什么。也许你可以详细说明一下,或者给我指一下关于这个主题的其他资源或帖子?非常感谢你花时间写下如此详细、清晰和简洁的解释。因此,我觉得我对事物的“为什么”有了更好的理解。
array = []
for i in range(num_elements):
array.append(True)