和,或不,对于具有复杂数据对象的多个列表,使用pythonic方式

时间:2017-06-10 20:04:15

标签: python python-3.x boolean-operations

我有多个包含复杂对象的列表。我想对它们进行布尔操作 AND OR NOT

AND :结果列表将包含所有使用的源列表中存在的所有对象。应该没有重复。

OR :结果列表应包含所有已使用源列表中的所有对象。应该没有重复。

:结果列表应仅包含源列表中不存在于非列表中的现有对象。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# the "complex data"
class Person:
    def __init__(self, name):
        # assume the 'name' as unique
        self.name = name

# create example data
mylistA = [Person('Anna'),
           Person('Bob'),
           Person('Jane'),
           Person('Alfred')]

mylistB = [Person('Simon'),
           Person('Anna'),
           Person('Doris'),
           Person('Bob')]

mylistC = [Person('Bob'),
           Person('Rosi'),
           Person('Becky'),
           Person('Anna')]

mylistD = [Person('Alfred'),
           Person('Bob'),
           Person('Chris'),
           Person('Susi')]

def doAND(some_lists):
    pass

def doOR(some_lists):
    pass

def doNOT(one_list, not_list):
    pass

# should result in 'Anna', 'Bob'
resultAND = doAND([mylistA, mylistB, mylistC])
print(resultAND)

# should result in 'Anna', 'Bob', 'Jane', 'Alfred', 'Simon', 'Doris', 'Rosi',
# 'Becky'
resultOR = doOR([mylistA, mylistB, mylistC])
print(resultOR)

# 'Anna'
resultNOT = doNOT(resultAND, mylistD)
print(resultNOT)

背景资料:"复杂对象"在真实场景中是sqlalchemy对象。他们的身份"在我的例子的上下文中,这不是主键。他们的身份"是基于成员的共同形成(简单的例子:"名字","姓氏","生日和#34;)。

3 个答案:

答案 0 :(得分:1)

这是您可以考虑的建议。将list表示为dict。这使得执行这些基于集合的操作变得更容易(在一定程度上)。

mydictA = {x.name : x for x in mylistA } 

无论如何,回到原来的问题。 doAND基本上是交叉操作。 doOR是一个联盟,doNOT是一个差异操作。

以下是doAND的示例。你应该能够弄清楚其余部分。

def doAND(list1, list2):
    dict1 = {x.name : x for x in list1}
    dict2 = {x.name : x for x in list2}
    common_names = set(dict1.keys()).intersection(set(dict2.keys()))
    return [dict1[x] for x in common_names if x in dict1] + [dict2[x] for x in common_names if x in dict2]
对于doOR,您需要set1.union(set2),对于doNOT,您应该set1 - set2

希望这有帮助。

答案 1 :(得分:1)

您应该使用set而不是列表。 这样可以避免重复,并以方便的方式提供所有操作:

a=[1,2,3,4,5]
b=[1,2,3]

a=set(a)
b=set(b)

# OR
a | b # [1,2,3,4,5]

# AND
a & b # [1,2,3]

# NOT
a - b # [4,5]

即使对于复杂的数据类型,您也可以使用它。他们需要满足两个标准:

  • __eq__需要实施
  • __hash__需要实施

该集需要__eq__来查找重复项。但是,如果您只实施__eq__,则会删除默认的__hash__实施。

这是因为__eq____hash__需要保持一致。 所以你需要重新实现__hash__

您对内置hash()函数的使用实际上比使用hashlib的版本好得多。所以我更新了。 令人惊讶的是,__hash__的实现并未提供__eq__的隐式实现,即使具有相同散列的对象必须相等也是不变的。因此,__eq____hash__都需要实施。在此答案的先前版本中,这是错误的。

由于性能原因,可能需要再次实施__eq__运算符。我不知道hash()函数的速度有多快,但如果你的集合变大,那么直接比较名称可能是一种有用的优化,而不是先对它们进行散列。

class Person:
    def __init__(self, name):
        # assume the 'name' as unique
        self.name = name

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return self.name == other.name
        # return hash(self) == hash(other)

    def __repr__(self):
        return self.name


persons = [Person("a"), Person("b"), Person("a")]

print(persons)  # [a, b, a]

persons_set= set(persons)

print(persons_set) # [a, b]

答案 2 :(得分:1)

感谢@ criket_007给了我正确的提示。 Python非常简单!只需为 complexe数据对象创建运算符。然后,您可以将它们视为order/allow/deny

这是更新的例子

set