作为一个蟒蛇新手我需要解决这个非常简单的事情。 说我上课了:
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方式”?
编辑:
需要保留列表中的事件元素的顺序
不想创建新的事件实例副本
答案 0 :(得分:2)
您真正需要的是一个字典,其中键是eid
,而项目是您的所有事件。我使用了集合中的defaultdict
来为字典提供一个默认项目 - 在本例中是一个列表。
from collections import defaultdict
d = defaultdict(list)
for i in [e1,e2,e3,e4]:
d[i.eid].append(i.events[0])
答案 1 :(得分: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>)]
其中key
是您的分组条件,values
是匹配项的迭代器。在此代码key
= eid
属性(attrgetter("eid")
)和values
=具有相同eid
值的所有项目。
starmap
与泛型map
的行为相同,但是:a)返回迭代器而不是list,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
属性的值(即事件列表)。
答案 2 :(得分: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__
个eid
和events
作为参数,那么这可以简化...
grouped_events = [Event(i,v) for (i,v) in d.items()]
答案 3 :(得分: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')]]
你认为可以进一步简化吗?