类对象的选择性比较

时间:2015-07-20 13:47:03

标签: python comparison

我需要对类对象进行多次比较。但是,只有选定字段的值才能进行比较,即:

class Class:
    def __init__(self, value1, value2, value3, dummy_value):
        self.field1 = value1
        self.field2 = value2
        self.field3 = value3
        self.irrelevant_field = dummy_value

obj1 = Class(1, 2, 3, 'a')
obj2 = Class(1, 2, 3, 'b') #compare(obj1, obj2) = True
obj3 = Class(1, 2, 4, 'a') #compare(obj1, obj3) = False

目前我这样做:

def dumm_compare(obj1, obj2):
    if obj1.field1 != obj2.field1:
        return False
    if obj1.field2 != obj2.field2:
        return False
    if obj1.field3 != obj2.field3:
        return False
    return True

由于我的相关字段的实际数量大于10,因此这种方法会导致相当庞大的代码。这就是为什么我尝试这样的事情:

def cute_compare(obj1, obj2):
    for field in filter(lambda x: x.startswith('field'), dir(obj1)):
        if getattr(obj1, field) != getattr(obj2, field):
            return False
    return True

代码紧凑;然而,性能受到严重影响:

import time

starttime = time.time()
for i in range(100000):
    dumm_compare(obj1, obj2)
print('Dumm compare runtime: {:.3f} s'.format(time.time() - starttime))

starttime = time.time()
for i in range(100000):
    cute_compare(obj1, obj2)
print('Cute compare runtime: {:.3f} s'.format(time.time() - start time))

#Dumm compare runtime: 0.046 s
#Cute compare runtime: 1.603 s

有没有办法更有效地实施选择性对象比较?

修改 实际上我需要几个这样的函数(通过不同的,有时重叠的字段集来比较对象)。这就是我不想覆盖内置类方法的原因。

2 个答案:

答案 0 :(得分:1)

dir()不仅包括实例属性,还会遍历类层次结构。因此,它比这里需要做的工作多得多; dir()实际上只适用于调试任务。

坚持使用vars(),或许与any()结合使用:

def faster_compare(obj1, obj2):
    obj2_vars = vars(obj2)
    return all(value == obj2_vars[field]
               for field, value in vars(obj1).items() if field.startswith('field'))

vars()返回仅包含实例属性的字典;在上面的生成器表达式中,我使用dict.items()方法在一个步骤中访问属性名称及其值。

我替换了getattr()的{​​{1}}方法调用以使用相同的字典方法,这样可以保存每次使用的framestack push和pop,因为密钥查找可以完全用字节码(C代码)处理。请注意,这确实假设您没有使用属性;只会列出实际的实例属性。

这种方法仍然需要做更多的工作而不是硬编码obj2分支,但它至少没有表现得那么糟糕:

if

答案 1 :(得分:1)

如果某个特定比较集中的所有实例都存在字段, 尝试保存列表以与班级进行比较。

def prepped_compare(obj1, obj2):
    li_field = getattr(obj1, "li_field", None)
    if li_field  is None:
        #grab the list from the compare object, but this assumes a 
        #fixed fieldlist per run.
        #mind you getattr(obj,non-existentfield) blows up anyway
        #so y'all making that assumption already
        li_field = [f for f in vars(obj1) if f.startswith('field')]
        obj1.__class__.li_field = li_field

    for field in li_field:
        if getattr(obj1, field) != getattr(obj2, field):
            return False
    return True    

或预先计算外部,更好

def prepped_compare2(obj1, obj2, li_field):

    for field in li_field:
        if getattr(obj1, field) != getattr(obj2, field):
            return False
    return True    


starttime = time.time()
li_field = [f for f in vars(obj1) if f.startswith('field')]
for i in range(100000):
    prepped_compare2(obj1, obj2, li_field)
print('prepped2 compare runtime: {:.3f} s'.format(time.time() - starttime))

输出:

Dumm compare runtime: 0.051 s
Cute compare runtime: 0.762 s
prepped compare runtime: 0.122 s
prepped2 compare runtime: 0.093 s

重。重写 eq ,我很确定你可以拥有类似的东西。

def mycomp01(self, obj2) #possibly with a saved field list01 on the class
def mycomp02(self, obj2) #possibly with a saved field list02 on the class

#let's do comp01.
Class.__eq__ = mycomp01
run comp01 tests
Class.__eq__ = mycomp02
run comp02 tests