2

作为一个 python 新手,我需要解决这个非常简单的事情。假设我有一堂课:

class Event():
  eid = 0
  events = []

  def __repr__(self):
    return "id:"+str(self.eid) + "=>" + str(self.events)

  def __str__(self):
    return self.__repr__()

让我们创建一些实例并将它们保存到列表中

eventset = list()
e1 = Event()
e1.eid = 0
e1.events = [('1','2','3','A')]

e3 = Event()
e3.eid = 1
e3.events = [('4','5','6','A')]

e2 = Event()
e2.eid = 0
e2.events = [('7','8','9','A')]

e4 = Event()
e4.eid = 1
e4.events = [('10','11','12','A')]

eventset.append(e1,e2,e3,e4)

打印事件集给出:

[id:0=>[('1', '2', '3', 'A')], id:0=>[('7', '8', '9', 'A')], id:1=>[('4', '5', '6',   'A')], id:1=>[('10', '11', '12', 'A')]]

我想创建一个新列表,如下所示:

[id:0=>[('1', '2', '3', 'A'),('7', '8', '9', 'A')], id:1=>[('4', '5', '6','A'),('10', '11', '12', 'A')]]

如何以优雅的“Pythonic 方式”做到这一点?

编辑:

  1. 需要保留列表中 Event 元素的顺序

  2. 不想创建新的 Event 实例副本

4

4 回答 4

2

您真正需要的是一个字典,其中键是 theeid并且项目是您的所有事件。我使用defaultdictfrom collections 给字典一个默认项——在本例中是一个列表。

from collections import defaultdict

d = defaultdict(list)

for i in [e1,e2,e3,e4]:
   d[i.eid].append(i.events[0])
于 2012-08-27T11:22:37.083 回答
2

我建议您“升级”Event课程:

class Event(object):  # <-- one change
    eid = 0
    events = []

    def __init__(self, eid=0, events=None): # <-- second change
        self.eid = eid
        if events is not None: self.events = list(events)

    def __repr__(self):
        return "id:"+str(self.eid) + "=>" + str(self.events)

    def __str__(self):
        return self.__repr__()

下一个:

from operator import add, attrgetter
from itertools import starmap, groupby

merge_event = lambda e, events: Event(e, reduce(add, map(attrgetter("events"), events), []))
list(starmap(merge_event, groupby([e1,e2,e3,e4], attrgetter("eid"))))

这是怎么回事

groupby返回带有元组列表的迭代器: ( key, values):

>>> list(groupby([e1,e2,e3,e4], attrgetter("eid")))
[(0, <itertools._grouper object at 0x105d96bd0>), (1, <itertools._grouper object at 0x105d96f10>)]

wherekey是您的分组标准,并且values是匹配项的迭代器。在此代码中key=eid属性 ( attrgetter("eid")) 和values= 具有相同eid值的所有项目。

starmap作用与泛型相同,map但:a) 返回迭代器而不是列表,b) 使用分隔参数 ( f(*(key,value)) = f(key, values)) 调用给定的回调函数。我们创建了特殊的函数merge_event来操作groupby输出。

merge_event将 ( key, values) 元组作为参数并生成一个Event对象。有了key(实际上是eid)一切都清楚了。要创建事件列表,我使用reduce带有运算符的函数(来自模块的add函数表示)。operator它以这种方式工作:

>>> reduce(add, [[1,2,3], ["A","B","C"]], [])
[1, 2, 3, 'A', 'B', 'C']

最后,仅map(attrgetter("events"), events)收集Event对象列表的属性值events(即事件列表)。

于 2012-08-27T11:36:58.893 回答
0

所以我认为我找到了非常好的和非常优雅的解决方案。请看一下并评论/简化。

我创建了一个迭代器,仅当尚未返回此 eid 时才会返回带有 eid 的元素。

class first_unique_iter(object):
  def __init__(self, mylist):
    self.eventset = mylist
    self.i = iter(mylist)
    self.used = []

  def __iter__(self):
    return self

  def next(self):
    element = self.i.next()
    if element.eid not in self.used:
        self.used.append(element.eid)
        return element
    else:
        return self.next()

然后是逻辑:

def slice_by_id(event, eventset):
   return [e for e in eventset if e.eid == event.eid]

def reduce_2one(x,y):
   x.events.extend(y.events)
   return x

final = [reduce(reduce_2one, slice_by_id(event,eventset)) for event in  first_unique_iter(eventset)]

因此,对于每一个具有唯一 eid 的第一个事件,我们使用这个新的迭代器运行 list comp。拥有每个事件的列表,我们需要从具有相同 eid 的事件中附加事件列表。这是在由 eid 列表切片调用的 reduce() 函数中完成的。

print final
>>> [id:0=>[('1', '2', '3', 'A'), ('7', '8', '9', 'A')], id:1=>[('4', '5', '6', 'A'), ('10', '11', '12', 'A')]]

你认为可以进一步简化吗?

于 2012-08-27T20:34:41.813 回答
0

@Burkan Khalid 的解决方案是最简单的。

想象一下,您可以将输出字典d转换为另一个事件列表:

grouped_events = []
for (i, v) in d:
    e = Event()
    e.eid = i
    e.events = v
    grouped_events.append(e)

当然,如果你的Event班级有适当__init__的 aeidevents作为参数,那可以简化......

grouped_events = [Event(i,v) for (i,v) in d.items()]
于 2012-08-27T11:30:40.587 回答