【问题标题】:Python 3: Merging list of objects with different attributes givenPython 3:合并具有不同属性的对象列表
【发布时间】:2019-11-18 11:09:43
【问题描述】:

我有一种情况(下面是简化的基本示例),其中我有两个列表,其中包含具有不同属性的自定义对象。在一个列表中,给出了一个属性(另一个是 0 或 None 或空列表),另一个列表包含另一个属性的值。这两个列表不一定一样长,即可能有一些不匹配的条目。我喜欢的是一个新列表(或修改现有列表之一),其中包含所有唯一对象,其属性值取自给定位置。

class human:
    def __init__(self, name, age=0, height=0):
        self.name = name
        self.age = age
        self.height = height

list_1 = [human("Alice", age=25),     human("Bob", age=30),    human("Steve", age=21)]
list_2 = [human("Alice", height=170), human("Bob", height=185)]

complete_list = merge(list_1, list_2)

所需的输出将是一个等于的列表:

complete_list = [human("Alice", age=25, height=170), human("Bob", age=30, height=185), human("Steve", age=21)]

当然,我可以遍历两个列表并检查匹配项,然后检查我的值在哪里,然后更新它不在的地方等等,但这很丑不是吗?

【问题讨论】:

    标签: python list object merge


    【解决方案1】:

    您的代码假定名称是唯一的,我们可以利用这一点:

    class human:
        def __init__(self, name, age=0, height=0):
            self.name = name
            self.age = age
            self.height = height
    
        def merge(self, other):
            if not self.age:
                self.age = other.age
            if not self.height:
                self.height = other.height
    
        def __repr__(self):
            return 'human(name={}, age={}, height={})'.format(self.name, self.age, self.height)
    
    def merge_lists(list_1, list_2):
        output = {h.name: h for h in list_1}
        for h in list_2:
            try:
                output[h.name].merge(h)
            except KeyError:
                output[h.name] = h
        return list(output.values())
    
    list_1 = [human("Alice", age=25), human("Bob", age=30), human("Steve", age=21)]
    list_2 = [human("Alice", height=170), human("Bob", height=185)]
    
    print(merge_lists(list_1, list_2))
    

    输出

    [human(name=Alice, age=25, height=170),
     human(name=Bob, age=30, height=185),
     human(name=Steve, age=21, height=0)]
    

    如果我们实现 HumanList 类,这可以更进一步:

    from collections import UserList
    from copy import deepcopy
    
    class human:
        def __init__(self, name, age=0, height=0):
            self.name = name
            self.age = age
            self.height = height
    
        def merge(self, other):
            if not self.age:
                self.age = other.age
            if not self.height:
                self.height = other.height
    
        def __repr__(self):
            return 'human(name={}, age={}, height={})'.format(self.name, self.age, self.height)
    
    class HumanList(UserList):
        def merge_lists(self, other_list):
            output = {h.name: deepcopy(h) for h in self}
            for h in other_list:
                try:
                    output[h.name].merge(h)
                except KeyError:
                    output[h.name] = h
            return HumanList(output.values())
    
    list_1 = HumanList([human("Alice", age=25), human("Bob", age=30), human("Steve", age=21)])
    list_2 = HumanList([human("Alice", height=170), human("Bob", height=185)])
    
    merged_list = list_1.merge_lists(list_2)
    print(merged_list)
    

    输出

    [human(name=Alice, age=25, height=170),
     human(name=Bob, age=30, height=185),
     human(name=Steve, age=21, height=0)]
    

    【讨论】:

    • 看起来不错,只有一个问题:except 子句中的output[h.name] = h 真的有必要吗?似乎可以正常通过,因为从创建字典时就已经存在 hs。还是我错过了什么?
    • @VY_CMa 如果list_2 包含名称未出现在list_1 中的对象,则这是必要的,这是output[h.name] 可以引发KeyError 的唯一情况
    【解决方案2】:
    class human:
        def __init__(self, name, age=0, height=0):
            self.name = name
            self.age = age
            self.height = height
    
    list_1 = [human("Alice", age=25),     human("Bob", age=30),    human("Steve", age=21)]
    list_2 = [human("Alice", height=170), human("Bob", height=185)]
    
    def merge(lst1, lst2):
        d = {}
    
        for h in lst1:
            d[h.name] = {'age': h.age}
    
        for h in lst2:
            d[h.name].update({'height': h.height})
        return d
    
    merge(list_1, list_2)
    

    返回:

    {'Alice': {'age': 25, 'height': 170}, 'Bob': {'age': 30, 'height': 185}, 'Steve': {'age': 21}}
    

    然后您可以从字典中创建一个列表以转换为人类列表。

    humans_dict = merge(list_1, list_2)
    humans_lst = [human(k, v.get('age', 0), v.get('height', 0)) for k, v in d.items()]
    

    【讨论】:

      【解决方案3】:

      您的问题中缺少的是您按人名对不同的条目进行分组。

      我会这样解决:

      people = dict()
      for l in [list_1,list_2]:
        for h in l:
          if h.name not in people:
            people[h.name] = h
          else:
            existingPerson = people[h.name]
            if h.age:
              existingPerson.age = h.age
            if h.height:
              existingPerson.height = h.height
      

      如果你有很多属性,我会这样做:

      people = dict()
      for l in [list_1,list_2]:
        for h in l:
          if h.name not in people:
            people[h.name] = h
          else:
            existingPerson = people[h.name]
            for a in ['age','height']:
              v = getattr(h,a)
              if v:
                setattr(existingPerson,a,v)
      

      最后你创建了完整的列表:

      complete_list = list(people.values())

      【讨论】:

        猜你喜欢
        • 2022-11-15
        • 2021-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-02
        • 1970-01-01
        • 2020-06-27
        • 1970-01-01
        相关资源
        最近更新 更多