Python:将可迭代对象转换为列表:'list(x)'与完整切片' x [:] '

3

我想知道在Python中将可迭代对象转换为列表的这两种方式是否有区别:

  • using the list() constructor:

    my_list = list(my_iterable)
    
  • using a full slice:

    my_list = my_iterable[:]
    

在实现上有什么不同吗?如果有,性能如何?Python 2与Python 3之间有任何变化吗?

当然,我意识到构造函数版本的可读性要好得多。

3个回答

10

list(thing)可以将一个对象转换成列表。

thing[:]可以得到任何类型的对象。

换句话说,第二种选项仅适用于特定类型的对象(你没有提到你实际使用的哪些类型)。

编辑: thing[:]的一个有用功能是,当它被支持时,它通常会引用“thing的所有元素”,这可以在不更改指向thing的对象的情况下进行修改。例如:

thing[:] = [1,2,3]

即使thing本来不是列表,也会将[1,2,3]赋值到thing中。

list(thing) = [1,2,3]

毫无意义,而且:

thing = [1,2,3]

使thing引用一个新列表,而不考虑其先前的类型。


我通常总是使用 list(thing),完整的切片仅在 Code Golf 中偶尔看到。所以我实际上并不真正知道切片操作符仅适用于有限的可迭代子集。感谢你的教导,我将继续像往常一样使用 list(thing)。但是,那么完整的切片操作符 [:] 是否真的有必要呢?调用 list(my_list) 也会创建一个具有不同 id 的新实例。 - Byte Commander
@ByteCommander:在现实世界中,[:] 的主要用途是就地分配列表或数组的所有元素。例如,x = [1,2,3]; x = [4,5,6] 会为 x 第二次引用创建一个完全不同的对象。但是,x = [1,2,3]; x[:] = [4,5,6] 使 x 引用相同的列表对象,并覆盖其内容。这在 NumPy 中经常很有用。我已更新我的答案以反映其中一些内容。 - John Zwinck
哦,太酷了!我还没有听说过给切片赋值。感谢您提供这些见解。 - Byte Commander

10

并不是所有的对象都支持切片操作,比如生成器:

(x for x in range(5))[:]   # raises an error

因此,list更为通用。 [:]主要只是复制列表的一种方式。

[:]之所以可行,主要是因为它是通过省略冒号前面的索引(例如[:3])和省略后面的索引组合而成的,这两个都是有用的。如果添加一个步长,例如[::-1],那么它也很有用,这是一个常见的字符串反转的方法。对于列表本身来说,[:]并没有什么用处,但在某些情况下,它可能会稍微快一点,就像我在Padriac的答案中所评论的那样,尽管可读性通常比这种微小的优化更重要。

对于非列表对象,[:]理论上可以根据您告诉它要执行的操作而做任何事情:

class A(object):
    def __getitem__(self, item):
        return 3

print A()[:]  # prints 3

但你可能要避免使用这样的定义。


那么,完整的切片操作符[:]真的有必要吗?调用list(my_list)也会创建一个具有不同id的新实例,这与切片的行为相同。 - Byte Commander

1
我假设my_iterable始终是一个列表或列表的子类,或者这两种方法可能会产生完全不同的结果:
在Python 2中,调用列表比切片略快:
In [2]: l = list(range(1000000))

In [3]: timeit l[:]
100 loops, best of 3: 10.8 ms per loop

In [4]: timeit list(l)
100 loops, best of 3: 9.45 ms per loop

Python3 的结果非常相似:
In [1]: l = list(range(1000000))

In [2]: timeit list(l)
100 loops, best of 3: 9.54 ms per loop

In [3]: timeit l[:]
100 loops, best of 3: 10.6 ms per loop

[:] 用于切片支持切片的任何元素,尝试在 my_iterable 不是列表时调用 list 或使用 [:] 毫无意义,因为它们基于使用对象的不同而执行非常不同的操作。

In [2]: i = (1,2,3,4,5)

In [3]: list(i)
Out[3]: [1, 2, 3, 4, 5]

In [4]: i[:]
Out[4]: (1, 2, 3, 4, 5)

In [5]: i = "hello world"

In [6]: list(i)
Out[6]: ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']

In [7]: i[:]
Out[7]: 'hello world'

如果你将开头改为range(10),那么结果就会被反转。[:]避免了查找list这个名称,所以如果你需要复制许多小列表,速度会更快。 - Alex Hall

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