给定一个列表
old_list = [obj_1, obj_2, obj_3, ...]
我想创建一个列表:
new_list = [[obj_1, obj_2], [obj_3], ...]
如何判断两个Python对象的特定属性是否相等?
我可以编写一些 for
循环和 if
条件语句,但这样很丑陋。有没有更符合Python风格的方式来处理呢?顺便说一下,这些对象的属性都是字符串类型。
或者,如果是包含元组(长度相同)的列表而不是对象,则也需要一种解决方案。
给定一个列表
old_list = [obj_1, obj_2, obj_3, ...]
我想创建一个列表:
new_list = [[obj_1, obj_2], [obj_3], ...]
如何判断两个Python对象的特定属性是否相等?
我可以编写一些 for
循环和 if
条件语句,但这样很丑陋。有没有更符合Python风格的方式来处理呢?顺便说一下,这些对象的属性都是字符串类型。
或者,如果是包含元组(长度相同)的列表而不是对象,则也需要一种解决方案。
defaultdict
就是这样实现的。
尽管for
循环通常是必不可少的,但if
语句则不是。
from collections import defaultdict
groups = defaultdict(list)
for obj in old_list:
groups[obj.some_attr].append(obj)
new_list = groups.values()
dict
的使用与itertools.groupby
的答案结合起来,甚至不需要使用defaultdict
。 - JABlist(groups.values())
来实际返回OP想要的内容吗?我的意思是,否则,如果调用new_list[0]
,她会得到TypeError:'dict_values' object does not support indexing
(至少在我的机器上)。 - sup以下是两种情况,它们都需要以下导入:
import itertools
import operator
你将使用 itertools.groupby 和 operator.attrgetter 或 operator.itemgetter。对于按 obj_1.some_attr == obj_2.some_attr
进行分组的情况:get_attr = operator.attrgetter('some_attr')
new_list = [list(g) for k, g in itertools.groupby(sorted(old_list, key=get_attr), get_attr)]
对于a[some_index] == b[some_index]
:
get_item = operator.itemgetter(some_index)
new_list = [list(g) for k, g in itertools.groupby(sorted(old_list, key=get_item), get_item)]
注意需要进行排序,因为itertools.groupby
会在键值改变时创建一个新的分组。
注意你可以使用这种方法来创建类似于S.Lott答案中的dict
,但不必使用collections.defaultdict
。
使用字典推导式(仅适用于Python 3+,可能也适用于Python 2.7,但我不确定):
groupdict = {k: g for k, g in itertools.groupby(sorted_list, keyfunction)}
对于早期版本的Python,或者作为更加简洁的替代方法:
groupdict = dict(itertools.groupby(sorted_list, keyfunction))
groupdict = {k: g for k, g in itertools.groupby(sorted_list, keyfunction)}
应该改为 groupdict = {k: list(g) for k, g in itertools.groupby(sorted_list, keyfunction)}
,因为 g
只是一个迭代器。 - undefined你也可以尝试使用itertools.groupby。请注意,下面的代码仅供参考,应根据您的需求进行修改:
data = [[1,2,3],[3,2,3],[1,1,1],[7,8,9],[7,7,9]]
from itertools import groupby
# for example if you need to get data grouped by each third element you can use the following code
res = [list(v) for l,v in groupby(sorted(data, key=lambda x:x[2]), lambda x: x[2])]# use third element for grouping
groupby
之前进行排序。 - JAB最近,我也遇到了类似的问题。感谢上面提供的解决方案。我对上述方法的计算时间进行了小比较。在我的实现中,我保留字典,因为看到键很好。使用defaultdict
的方法获胜。
from collections import defaultdict
import time
import itertools
import pandas as pd
import random
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
def method_with_dict(people):
groups={}
for person in people:
if person.age in groups:
groups[person.age].append(person)
else:
groups[person.age]=[person]
return groups
def method_with_defaultdict(people):
groups=defaultdict(list)
for person in people:
groups[person.age].append(person)
return groups
def group_by_age_with_itertools(people):
people.sort(key=lambda x: x.age)
groups={}
for age,group in itertools.groupby(people,key=lambda x: x.age):
groups[age]=list(group)
return groups
def group_by_age_with_pandas(people):
df=pd.DataFrame([(p.name,p.age) for p in people],columns=["Name","Age"])
groups=df.groupby("Age")["Name"].apply(list).to_dict()
return {k: [Person(name,k) for name in v] for k,v in groups.items()}
if __name__ == "__main__":
num_people=1000
min_age,max_age=18,80
people=[Person(name=f"Person {i}",age=random.randint(min_age,max_age)) for i in
range(num_people)]
N=10000
start_time=time.time()
for i in range(N):
result_defaultdict=method_with_defaultdict(people)
end_time=time.time()
print(f"method_with_defaultdict: {end_time - start_time:.6f} seconds")
start_time=time.time()
for i in range(N):
result_dict=method_with_dict(people)
end_time=time.time()
print(f"method_with_dict: {end_time - start_time:.6f} seconds")
start_time=time.time()
for i in range(N):
result_itertools=group_by_age_with_itertools(people)
end_time=time.time()
print(f"method_with_itertools: {end_time - start_time:.6f} seconds")
start_time=time.time()
for i in range(N):
result_pandas=group_by_age_with_pandas(people)
end_time=time.time()
print(f"method_with_pandas: {end_time - start_time:.6f} seconds")
method_with_defaultdict: 0.954309 seconds
method_with_dict: 1.301710 seconds
method_with_itertools: 1.868009 seconds
method_with_pandas: 34.422366 seconds