【问题标题】:Unpacking iterable into other iterable?将可迭代解包到其他可迭代中?
【发布时间】:2013-02-07 14:49:24
【问题描述】:

从 ASCII 文件中读取数据时,我发现自己在做这样的事情:

(a, b, c1, c2, c3, d, e, f1, f2) = (float(x) for x in line.strip().split())
c = (c1, c2, c3)
f = (f1, f2)

如果我每行有确定数量的元素(我这样做)¹并且只有一个多元素条目要解包,我可以使用类似 `(a, b, *c, d, e) = .. .' (Extended iterable unpacking)。

即使我不这样做,我当然也可以将上面示例中的两个多元素条目中的 一个 替换为带星号的组件:(a, b, *c, d, e, f1, f2) = ...

据我所知,itertools 在这里不能立即使用。

是否有任何替代上述三行代码的替代方法可能被认为是“更 Python 的”,原因我可能不知道?

¹它是确定的,但仍因行而异,模式对于numpys 函数loadtxtgenfromtxt 来说太复杂了。

【问题讨论】:

  • 添加了 python-3.0x 标记,因为 pep3132(扩展可迭代解包)在 python 3.0 及更高版本上实现。

标签: python python-3.x iterable-unpacking


【解决方案1】:

如果你真的经常使用这样的语句,并且想要代码的最大灵活性和可重用性而不是经常编写这样的模式,我建议为它创建一个小函数。只需将其放入某个模块并导入即可(您甚至可以导入我创建的脚本)。

有关用法示例,请参阅if __name__=="__main__" 块。诀窍是使用组 ID 列表将 t 的值组合在一起。这个id列表的长度至少应该和t的长度一样。

我只会解释主要的概念,如果你有什么不懂的,尽管问。

我使用来自 itertools 的 groupby。尽管在这里如何使用它可能并不简单,但我希望它很快就会被理解。

作为key-function,我使用了一种通过工厂函数动态创建的方法。这里的主要概念是“闭包”。组 ID 列表被“附加”到内部函数 get_group。因此:

  • 该列表特定于对extract_groups_from_iterable 的每次调用。可以多次使用,不使用全局变量

  • 此列表的状态在对同一 get_group 实例的后续调用之间共享(请记住:函数也是对象!所以在执行脚本期间我有两个 get_group 实例。

除此之外,我还有一个简单的方法可以从groupby 返回的组中创建列表或标量。

就是这样。

from itertools import groupby

def extract_groups_from_iterable(iterable, group_ids):
    return [_make_list_or_scalar(g) for k, g in 
                        groupby(iterable, _get_group_id_provider(group_ids))
           ]

def _get_group_id_provider(group_ids):
    def get_group(value, group_ids = group_ids):
        return group_ids.pop(0)
    return get_group

def _make_list_or_scalar(iterable):
    list_ = list(iterable)
    return list_ if len(list_) != 1 else list_[0]

if __name__ == "__main__":
    t1 = range(9)
    group_ids1 = [1,2,3,4,5,5,6,7,8]
    a,b,c,d,e,f,g,h = extract_groups_from_iterable(t1, group_ids1)

    for varname in "abcdefgh":
        print varname, globals()[varname]

    print

    t2 = range(15)
    group_ids2 = [1,2,2,3,4,5,5,5,5,5,6,6,6,7,8]
    a,b,c,d,e,f,g,h = extract_groups_from_iterable(t2, group_ids2)

    for varname in "abcdefgh":
        print varname, globals()[varname]

输出是:

a 0
b 1
c 2
d 3
e [4, 5]
f 6
g 7
h 8

a 0
b [1, 2]
c 3
d 4
e [5, 6, 7, 8, 9]
f [10, 11, 12]
g 13
h 14

再一次,这似乎有点矫枉过正,但如果这有助于您减少代码,请使用它。

【讨论】:

    【解决方案2】:

    为什么不直接切片一个元组?

    t = tuple(float(x) for x in line.split())
    c = t[2:5]  #maybe t[2:-4] instead?
    f = t[-2:]
    

    演示:

    >>> line = "1 2 3 4 5 6 7 8 9"
    >>> t = tuple(float(x) for x in line.split())
    >>> c = t[2:5]  #maybe t[2:-4] instead?
    >>> f = t[-2:]
    >>> c
    (3.0, 4.0, 5.0)
    >>> t
    (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
    >>> c = t[2:-4]
    >>> c
    (3.0, 4.0, 5.0)
    

    虽然我们讨论的是 Pythonic,但line.strip().split() 始终可以安全地写为line.split(),其中line 是一个字符串。 split 将在您不提供任何参数时为您去除空格。

    【讨论】:

    • 实际上,我有 7 个不同的元素,所以我需要做 a, b, c, d = t[:4]; e = t[4:6]; f, g, h = t[6:9]... 不确定这是否是一种改进。也许是。
    猜你喜欢
    • 2016-12-25
    • 2019-08-15
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 2015-07-23
    相关资源
    最近更新 更多