Python中range函数对列表有什么作用?

5

我无法弄清楚这里发生了什么。在range函数中添加引用,有点像在索引3处创建递归列表。

>>> x = range(3)
[0, 1, 2]
>>> x.append(x)
[0, 1, 2, [...]]
>>> x[3][3][3][3][0] = 5
[5, 1, 2, [...]]

然而,当我尝试这样做时:

 >>> x = range(3)
 [0, 1, 2]
 >>> x.append(range(3))
 [0, 1, 2, [0, 1, 2]]

我可以轻松推断第二种情况的原因,但不理解在 range 函数上附加引用对附加的列表有什么影响。

3
为什么这个标签同时标记了Python 2和3?range在2和3中的行为不同。 - interjay
2
在Python中,列表是按引用传递的。这意味着在第一种情况下,你正在附加指向x的指针,而不是x的值。因为x现在包含对x的指针,所以在该点递归调用它本身。 - Jacob H
@JacobH:说“列表是通过引用传递的”似乎暗示列表的行为可能与其他对象不同。实际上,所有对象都是以相同的方式传递的(尽管人们对描述这一点的正确短语有争议)。 - DSM
@interjay 我不小心做了那件事 :( - Rahul
2个回答

9
在Python2中,rangelistlist和大多数Python中的东西都是带有身份的对象
li = [0,1]
li[1] = li    # [0, [...]]
              # ^----v
id(li)        # 2146307756
id(li[1])     # 2146307756

由于您将列表放在自身中,因此您正在创建一个递归数据结构。


4
请注意,这是针对Python 2的特定情况。在Python 3中,范围完全不同,会返回一个惰性求值的'range'对象,而不是列表。 - MattDMo
@Karoly 谢谢你的回答,但我不明白为什么赋值 5 是有效的。根据我的理解,如果我使用 a[1][0] = 5,那么它意味着将 5 分配给索引为 1 的列表中索引为 0 的位置。 - Rahul
如果你的意思是:li[1]li,那么只需要将 li[0] 赋值为 5 就行了。 - Karoly Horvath

0

首先,这很奇怪,你可能不应该在实践中使用它。问题并不特定于range函数,而与引用有关。当你调用x.append(x)时,你实际上是说x[-1]是x。因此,当你修改x[0]时,你也会修改x[-1][0]x[-1][-1][0]等。

为了证明这不是特定于range的,你可以使用copy.copy

from copy import copy

x = range(1)
x.append(x)  # here x[1] is reference to x itself (same object)
print(x[0], x[1][0], x[1][1][0])
x[0] = 1
print(x[0], x[1][0], x[1][1][0])  # all values change

# 

x = range(1)
x.append(copy(x))  # x[1] is a copy of x at previous state (new object)
print(x[0], x[1][0])  # cannot call x[1][1][0] -> x[1][1] is an int
x[0] = 1
print(x[0], x[1][0])  # only the first value changes

输出:

(0, 0, 0)
(1, 1, 1)
(0, 0)
(1, 0)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接