简体   繁体   English

拆包迭代到其他迭代?

[英]Unpacking iterable into other iterable?

While reading data from a ASCII file, I find myself doing something like this: 从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)

If I have a determinate number of elements per line (which I do)¹ and only one multi-element entry to unpack, I can use something like `(a, b, *c, d, e) = ...' ( Extended iterable unpacking ). 如果我每行有确定数量的元素(我这样做)¹,并且只有一个要解包的多元素条目,则可以使用类似((a,b,* c,d,e)= ...'( 扩展的可迭代拆包( )。

Even if I don't, I can of course replace one of the two multi-element entries from the example above by a starred component: (a, b, *c, d, e, f1, f2) = ... . :即使我不,我可以当然由星号的部件代替从上面的例子中的两个多元件条目中的一个 (a, b, *c, d, e, f1, f2) = ...

As far as I can tell, the itertools are not of immediate use here. 据我所知,这里没有立即使用itertools

Are there any alternatives to the three-line code above that may be considered "more pythonic" for a reason I'm probably not aware of? 出于我可能不知道的原因,以上三行代码是否有其他替代方法可能被视为“更多pythonic”?

¹It's determinate but still varies per line, the pattern is too complicated for numpy s functions loadtxt or genfromtxt . ¹这很确定,但每行仍会有所不同,对于numpy的函数loadtxtgenfromtxt来说,模式太复杂了。

If you use such statements really often, and want maximum flexibility and reusability of code instead of writing such patterns really often, I'd propose creating a small function for it. 如果您真的经常使用这样的语句,并且想要最大的灵活性和代码的可重用性,而不是真的经常编写这种模式,那么我建议为其创建一个小函数。 Just put it into some module and import it (you can even import the script I created). 只需将其放入某个模块并导入(您甚至可以导入我创建的脚本)。

For usage examples, see the if __name__=="__main__" block. 有关用法示例,请参见if __name__=="__main__"块。 The trick is to use a list of group ids to group values of t together. 诀窍是使用组ID列表将t值组合在一起。 The length of this id list should be at least the same as the length of t . 此ID列表的长度至少应与t的长度相同。

I will only explain the main concepts, if you don't understand anything, just ask. 我只会解释主要概念,如果您什么都不懂,那就问一下。

I use groupby from itertools. 我从itertools使用groupby Even though it might not be straightforward how to use it here, I hope it might be understandable soon. 尽管在这里如何使用它可能并不容易,但我希望它很快就能理解。

As key -function I use a method I dynamically create via a factory-function. 作为key功能,我使用通过工厂功能动态创建的方法。 The main concept here is "closures". 这里的主要概念是“关闭”。 The list of group ids is being "attached" to the internal function get_group . 组ID列表正在“附加”到内部函数get_group Thus: 从而:

  • The list is specific to each call to extract_groups_from_iterable . 该列表特定于对extract_groups_from_iterable每次调用。 You can use it multiple times, no globals are used 您可以多次使用它,不使用全局变量

  • The state of this list is shared between subsequent calls to the same instance of get_group (remember: functions are objects, too! So I have two instances of get_group during the execution of my script. 此列表的状态在对get_group的同一实例的后续调用之间共享(请记住:函数也是对象!因此,在脚本执行期间,我有两个get_group实例。

Beside of this, I have a simple method to create either lists or scalars from the groups returned by groupby . 除此之外,我有一个简单的方法可以根据groupby返回的组创建列表或标量。

That's it. 而已。

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]

Output is: 输出为:

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

Once again, this might seem like overkill, but if this helps you reducing your code, use it. 再一次,这似乎有点过头了,但是如果这可以帮助您减少代码,请使用它。

Why not just slice a tuple? 为什么不只切片一个元组?

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

demo: 演示:

>>> 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)

While we're on the topic of being pythonic, line.strip().split() can always be safely written as line.split() where line is a string. 虽然我们的话题是line.strip().split() ,但line.strip().split()始终可以安全地写成line.split() ,其中line是字符串。 split will strip the whitespace for you when you don't give it any arguments. 如果不给任何参数, split将为您去除空格。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM