在列表中解压迭代器的 Pythonic 方法

Pythonic way to unpack an iterator inside of a list

我想弄清楚 pythonic 在列表中解压迭代器的方法是什么。

例如:

my_iterator = zip([1, 2, 3, 4], [1, 2, 3, 4])

我使用以下方法将我的迭代器解包到列表中:

1)

my_list = [*my_iterator]

2)

my_list = [e for e in my_iterator]

3)

my_list = list(my_iterator)

否 1) 是我最喜欢的方法,因为代码更少,但我想知道这是否也是 pythonic 方式。或者除了这 3 种 pythonic 方法之外,还有另一种方法可以实现这一点?

这可能是 Fastest way to convert an iterator to a list 的重复,但你的问题有点不同,因为你问的是哪个最 Pythonic。接受的答案是 list(my_iterator) 而不是 [e for e in my_iterator] 因为先验在 C 中运行。一位评论者建议 [*my_iterator]list(my_iterator) 快,所以您可能想测试一下。我的普遍投票是它们都同样是 Pythonic,所以对于您的用例,我会选择两者中速度更快的那个。也有可能旧答案已过时。

在进一步探索这个主题后,我得出了一些结论。

There should be one-- and preferably only one --obvious way to do it

(zen of python)

决定哪个选项是 "pythonic" 应该考虑一些标准:

  • 多么明确,
  • 简单,
  • 可读性强。

在所有标准中获胜的明显 "pythonic" 选项是选项编号 3):

list = list(my_iterator)

这就是为什么 "obvious" 没有 3) 是 pythonic 的原因:

  • 选项 3) 接近自然语言,使您 'instantly' 想想输出是什么。
  • 选项 2)(使用列表理解)如果你是第一次看到 那行代码会带你多读一点,然后付钱 多一点关注。例如,当我 想要添加一些额外的步骤(使用迭代调用函数 元素或使用 if 语句进行一些检查),所以当我看到 列表理解我检查内部或内部是否有任何可能的函数调用 对于任何 if 语句。
  • 选项 1)(使用 * 解包)星号运算符可能有点混乱 如果您不经常使用它,则有 4 个使用案例 Python 中的星号:

    1. 用于乘法和幂运算。
    2. 用于重复扩展列表类型的容器。
    3. 用于使用可变参数。 (所谓的“打包”)
    4. 用于打开容器。

另一个很好的论据是 python docs themselves, I have done some statistics to check which options are chosen by the docs, for this I've chose 4 buil-in iterators and everything from the module itertools(像 itertools. 一样使用)来查看它们是如何在列表中解包的:

  • 地图
  • 范围
  • 过滤器
  • 枚举
  • itertools.

浏览文档后我发现:0 个迭代器使用选项 1) 和 2) 在列表中解压,35 个使用选项 3)。

结论

The pythonic way to unpack an iterator inside of a list is: my_list = list(my_iterator)

如果我需要将列表转换为字典或将其用作循环或列表理解中的键值对,我倾向于使用 zip。

但是,如果这只是为了说明创建一个迭代器。为了清楚起见,我一定会投票给 #3

如果您对尽可能少的输入感兴趣,您实际上可以使用可迭代解包比 my_list = [*my_iterator] 更好地输入一个字符:

*my_list, = my_iterator

或(虽然这在字符数上只等于my_list = [*my_iterator]):

[*my_list] = my_iterator

(有趣的是它与my_list = [*my_iterator]的效果相同。)

然而,对于最 Pythonic 的解决方案,my_list = list(my_iterator) 显然是所有解决方案中最清晰和最易读的,因此应该被认为是最 Pythonic 的。

虽然拆包运算符 * 不常用于将单个可迭代对象拆包到列表中(因此 [*it]list(it) 可读性差一点),但它很方便并且在其他几种情况下更 Pythonic:

1。将可迭代对象解包为单个列表/元组/集合,添加其他值:

mixed_list = [a, *it, b]

这样比

更简洁高效
mixed_list = [a]
mixed_list.extend(it)
mixed_list.append(b)

2。将多个迭代器+值解包到列表/元组/集合中

mixed_list = [*it1, *it2, a, b, ... ]

这与第一种情况类似。

3。将可迭代对象解包到列表中,不包括元素

first, *rest = it

这会将 it 的第一个元素提取到 first 并将其余元素解压缩到列表中。甚至可以做到

_, *mid, last = it

这会将 it 的第一个元素转储到无关变量 _,将最后一个元素保存到 last,并将其余元素解压缩到列表 mid.

4。在一个语句中嵌套拆包多个级别的可迭代对象

it = (0, range(5), 3)
a1, (*a2,), a3 = it          # Unpack the second element of it into a list a2
e1, (first, *rest), e3 = it  # Separate the first element from the rest while unpacking it[1]

这也可以用在for语句中:

from itertools import groupby

s = "Axyz123Bcba345D"
for k, (first, *rest) in groupby(s, key=str.isalpha):
    ...