根据属性值

时间:2018-02-05 20:39:03

标签: python list

我有一个具有suit属性的对象数组,我想根据对象具有哪些对象分成子数组。我目前正在使用它:

    for c in cards:
        if c.suit.value == 0:
            spades.append(c)
        elif c.suit.value == 1:
            diamonds.append(c)
        elif c.suit.value == 2:
            clubs.append(c)
        else:
            hearts.append(c)

我尝试使用itertools.groupby,如下所示:

suits = [list(g) for g in intertools.groupby(cards, lambda x: x.suit.value)]

但这只是收益:

[[3, <itertools._grouper object at 0x000000000296B2E8>], ...]

我的第一种方法是有效的,我只想象有一个简单的pythonic one liner可以完成我需要的工作。

3 个答案:

答案 0 :(得分:5)

虽然它不是单行,但通过使用列表,我们可以使它更优雅:

spades, diamonds, clubs, hearts = collcard = [[] for _ in range(4)]

for c in cards:
    collcard[c.suit.value].append(c)

这里我们初始化一个包含四个空子列表的列表,然后我们将卡c附加到索引为c.suit.value的列表中。

我们使用iterable unpacking将第一个元素分配给spades,将第二个元素分配给diamonds等。

优点是我们避免排序(在 O(n log n)中工作)。因此,该算法具有时间复杂度 O(n)(假设附加列表的摊销成本为 O(1))。

虽然oneliner通常很优雅,但是编写这些内容时不应该付出太多努力,因为oneliner可能更难理解,或者对性能有重大影响。

答案 1 :(得分:4)

当迭代时,itertools.groupby产生tuple个对象:密钥和分组值的可迭代(允许对数据进行惰性求值)。

您刚刚将tuple转换为list,这不是很有用。

相反,你需要删除密钥(例如通过将其解压缩到_,告知你没有使用它的pythonic方式)并强制迭代值:

[list(g) for _,g in itertools.groupby(cards, lambda x: x.suit.value)]

现在,如果你想对进行分组,那么将sorted(cards)传递给groupby并不是非常有效,只是为了创造一个单行(单行)很好,但我更喜欢快节目)。您的方法有效,或者您也可以使用collections.defaultdict,甚至使用间接使用正确的颜色命名dict,例如:

import collections

cards = collections.defaultdict(list)
colors = ["spades","diamonds","clubs","hearts"]

for c in cards:
    cards[colors[c.suit.value]].append(c)

答案 2 :(得分:1)

如果您知道卡片组的大小,您可以做这样的事情,考虑是40卡牌:

allcards = sorted(cards, key=lambda x: x.suit.value)
spades, diamonds, clubs, hearts =  map(lambda x: allcards[x:x+10], range(0,40,10))

基本上你得到了一个甲板的分类版本,并将其带入与套装尺寸相对应的块中。如果它们从较低的索引到较高的索引,则从黑桃到黑格尔。