切片的分配点是什么?

时间:2019-06-19 20:02:58

标签: python

我在pip来源中找到了这一行:

sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path

据我了解,上面的代码行与下面的代码相同:

sys.path = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path

有一个区别:在第一种情况下,sys.path仍指向内存中的同一对象,而在第二种情况下,sys.path指向由两个现有对象创建的新列表。

另一件事是,第一种情况比第二种情况慢两倍:

>>> timeit('a[:] = a + [1,2]', setup='a=[]', number=20000)
2.111023200035561
>>> timeit('a = a + [1,2]', setup='a=[]', number=20000)
1.0290934000513516

我认为原因是,在切片分配的情况下,来自a的对象(对对象的引用)被复制到新列表中,然后再复制回到调整后的尺寸a

那么使用分片分配有什么好处?

4 个答案:

答案 0 :(得分:5)

如果对同一列表还有其他引用,并且您希望所有引用都接受更改,则分配给切片将非常有用。

因此,如果您执行以下操作:

bar = [1, 2, 3]
foo = bar
bar[:] = [5, 4, 3, 2, 1]
print(foo)

这将打印[5, 4, 3, 2, 1]。如果您选择这样做:

bar = [5, 4, 3, 2, 1]
print(foo)

输出将为[1, 2, 3]

答案 1 :(得分:3)

  

有一个区别:在第一种情况下,sys.path仍指向内存中的同一对象,而在第二种情况下,sys.path指向由两个现有对象创建的新列表。

正确:重点是,您要修改名称后面的对象,而不是名称。因此,所有其他引用同一对象的名称也将看到更改。

  

另一件事是,第一种情况比第二种情况慢两倍:

不是。切片分配执行复制。执行复制是O(n)操作,而执行名称分配是O(1)。换句话说,列表越大,副本越慢;而名称分配总是需要相同(较短)的时间。

答案 2 :(得分:1)

您的假设非常好!

在python中,变量是一个已设置为指向内存中对象的名称,从本质上讲,这就是使python成为动态类型语言的能力,即,您可以将变量与数字相同,然后将其重新分配给字符串等。

如此处所示,每当为变量分配新值时,您只是将名称指向内存中的另一个对象

>>> a = 1
>>> id(a)
10968800
>>> a = 1.0
>>> id(a)
140319774806136
>>> a = 'hello world'
>>> id(a)
140319773005552

(在CPython中,id指向其在内存中的地址)。

现在,您的问题sys.pathlist,而Python list是可变类型,因此意味着该类型本身可以更改,即

>>> l = []
>>> id(l)
140319772970184
>>> l.append(1)
>>> id(l)
140319772970184
>>> l.append(2)
>>> id(l)
140319772970184

即使我通过添加项目来修改列表,但list仍指向同一对象,并且按照python的本质,列表元素也仅是指向内存中不同区域的指针(元素为

>>> l
[1, 2]
>>> id(l[0])
10968800
>>> l[0] = 3
>>> id(l[0])
10968864
>>> id(l)
140319772970184

在重新分配给l[0]后,该元素的ID已更改。但再次没有。

看到分配给列表中的索引仅会更改列表元素指向的位置,现在您将了解,当我重新分配l而不重新分配时,我只是更改了l的位置在指向

>>> id(l)
140319772970184
>>> l = [4, 5, 6]
>>> id(l)
140319765766728

但是如果我重新分配所有l的索引,那么l会保留相同的对象,只有元素指向不同的位置

>>> id(l)
140319765766728
>>> l[:] = [7, 8, 9]
>>> id(l)
140319765766728

这还将使您了解为什么它变慢,因为python正在重新分配列表的元素,而不仅仅是将列表指向其他位置。

还有一点,如果您想知道行的结尾部分

sys.path[:] = ... + sys.path

具有相同的概念,python首先在=的右侧创建对象,然后将左侧的名称指向新对象,因此当python仍在上创建新列表时在右侧,sys.path本质上是原始列表,而python获取其所有元素,然后将所有新创建的元素重新分配给原始sys.path地址中的映射(因为我们使用了{ {1}})

现在不知道为什么pip使用[:]而不是重新分配,但我真的不知道,但是我相信为[:]重用内存中的同一对象可能会有好处。 br /> python本身也适用于小整数,例如

sys.path

>>> id(a) 10968800 >>> id(b) 10968800 >>> id(c) 10968800 ab都指向内存中的同一对象,即使它们都请求创建c并指向它,因为python知道小数字很可能会在程序中被大量使用(例如,在1循环中使用),因此它们会创建它并在整个过程中重复使用。
(您可能还会发现文件句柄的情况是python将回收而不是创建新的句柄。)

答案 3 :(得分:0)

是的,切片分配不会重新绑定,并且slice object是Python中的一种对象。您可以使用它进行设置和获取。

In [1]: a = [1, 2, 3, 4]

In [2]: a[slice(0, len(a), 2)]
Out[2]: [1, 3]

In [3]: a[slice(0, len(a), 2)] = 6, 6

In [4]: a[slice(0, len(a), 1)] = range(10)

In [5]: a
Out[5]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [6]: a[:] = range(4)

In [7]: a
Out[7]: [0, 1, 2, 3]