避免大量if ... else语句的Python方式?

问题描述

| 最近,它已经出现了好几次了,我想比以前更好地处理它:我有一系列要在对象和字典之间交叉引用的属性。如果它们之间的值不同,我想将object.attribute设置为dictionary [\'attribute \']值。我还想跟踪正在发生的变化。 现在,我的第一个想法是仅对每个属性使用if else语句,但是在编写了其中一些语句之后,很明显,我一次又一次地重写了相同的代码。必须有一种DRY方法来执行此操作,在该方法中,我仅指定每次更改的部分,然后遍历所有属性。 在生产代码中,有15个不同的属性,但是为了简单起见,我在下面的示例中仅使用2个属性。我对如何以一种聪明的方式做到这一点有所了解,但是我错过了实际设置object.attribute等于dictionary [\'attribute \']值的最后一步。
# Simulated data setup - not under my control IRL
class someClass:
    def __init__(self,name,version):
        self.name = name
        self.version = version

objA = someClass(\'Test1\',\'1.1\')        
dictA = {\'name\':\'Test1\',\'revision\':\'1.2\'}

# My code below        

# option 1 - a series of for loops
def updateAttributesSimple(obj,adict,msg):
    if obj.name == adict[\'name\']:
        msg.append(\'Name is the same\')
    else:
        msg.append(\'Name was updated from %s to %s\' % (obj.name,adict[\'name\']))
        obj.name = adict[\'name\']

    if obj.version == adict[\'revision\']:
        msg.append(\'Version is the same\')
    else:
        msg.append(\'Version was updated from %s to %s\' % (obj.version,adict[\'revision\']))
        obj.version = adict[\'revision\']        

# option 2 - trying to be cLever about this
def updateAttributesCLever(obj,msg):
    attributeList = ((\'Name\',obj.name,adict[\'name\']),(\'Version\',obj.version,adict[\'revision\']))

    for valTuple in attributeList:
        if valTuple[1] == valTuple[2]:
            msg.append(\'%s is the same\' % (valTuple[0]))
        else:
            msg.append(\'%s was updated from %s to %s\' % (valTuple[0],valTuple[1],valTuple[2]))
            # code to set valTuple[1] = valTuple[2] goes here,but what is it?
            # valTuple[1] = valTuple[2] attempts to set the desired value to a string,rather than the attribute of obj itself            


msg = [\'Updating Attributes simple way:\']
updateAttributesSimple(objA,dictA,msg)
print \'\\n\\t\'.join(msg)

#reset data
objA = someClass(\'Test1\',\'revision\':\'1.2\'}

msg = [\'Updating Attributes cLever way:\']
updateAttributesCLever(objB,dictB,msg)
print \'\\n\\t\'.join(msg)
这样的想法是,每当我需要添加一个属性时,我都可以更新要检查的属性列表,并且其余代码已经编写完毕。用Python的方式可以做到这一点?     

解决方法

setattr()
是您要寻找的:
attributeList = ((\'Name\',\'name\',\'name\'),(\'Version\',\'version\',\'revision\'))

for title,obj_attribute,dict_key in attributeList:
    obj_value = getattr(obj,obj_attribute)
    adict_value = adict[dict_key]

    if obj_value == adict_value:
        msg.append(\'%s is the same\' % (obj_value))
    else:
        msg.append(\'%s was updated from %s to %s\' % (title,obj_value,adict_value))

        setattr(obj,adict_value)
    ,这应该适用于您:
class X(object):
    def __init__(self):
        self.a = 1
        self.b = 2

x = X()

d = dict()
d[\'a\'] = 1
d[\'b\'] = 3

def updateAttributes(obj,dic):
    def update(name):
        val = dic[name]
        if getattr(obj,name)==val:
            print name,\"was equal\"
        else:
            print \"setting %s to %s\" % (name,val)
            setattr(obj,name,val)

    for name in [\'a\',\'b\']:
        update(name)

updateAttributes(x,d)
print x.a
print x.b
    ,您可能要考虑创建一个可以接受任意对象的函数,并将名称/值对的字典转换为更有意义的函数。这并不是严格的“ Python”策略,但由于支持闭包以及如何在后台处理对象,因此在Python中相当容易做到:
def checkUpdates( obj ):
    def updated( dictionaryPrevious,msg ):
        for name,value in dictionaryPrevious.items():
            if( obj.__dict__[name] == value ):
                msg.append(\'Name is the same\')
            else:
                msg.append(name + \'has been changed!\')
                obj.__dict__[name] = value
    return updated
我做一个假设,字典中的名称始终对应于对象变量。如果它们不相同,则需要进行映射。 编辑:
()
=>
[]
object
=>
obj
。谢谢你们。有时,您从一种语言转到另一种语言,这一切都变得混乱。     ,有几个答案很接近,但是要处理dict中的键名与相应对象的属性名不匹配的事实,您需要某种方式来处理。这可以通过添加另一个字典来轻松完成,该字典将dict中的键名映射到对象属性的名称。
class someClass:
    def __init__(self,version):
        self.name = name
        self.version = version

objA = someClass(\'Test1\',\'1.1\')
dictA = {\'name\':\'Test1\',\'revision\':\'1.2\'}
keymap = {\'name\':\'name\',\'revision\':\'version\'}

def updateAttributesGeneric(obj,adict,key2attr,msg):
    for key,value in adict.iteritems():
        attrname = key2attr[key]
        if getattr(obj,attrname) == value:
            msg.append(\'%s is the same\' % attrname)
        else:
            msg.append(\'%s has been changed\' % attrname)
            setattr(obj,attrname,adict[key])

msg = [\'Updating Attributes:\']
updateAttributesGeneric(objA,dictA,keymap,msg)
print \'\\n\\t\'.join(msg)

# Updating Attributes:
#   name is the same
#   version has been changed