避免大量if...else语句的Pythonic方法?
最近这个问题出现了好几次,我想更好地处理它。我有一系列的属性,需要在一个对象和一个字典之间进行对比。如果它们的值不一样,我想把对象的属性设置为字典中对应属性的值。同时,我也想记录下哪些地方发生了变化。
我最开始的想法是对每个属性都用一个if else语句,但写了几个之后,我发现我在重复写同样的代码。这肯定有一种更简洁的方法,可以只指定每次变化的部分,然后循环遍历所有属性。
在实际的代码中,有15个不同的属性,但为了简单起见,我下面的例子只用两个。我对如何聪明地做到这一点有一些想法,但我缺少最后一步,就是把对象的属性设置为字典中对应属性的值。
# 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, adict, 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','1.1')
dictA = {'name':'Test1','revision':'1.2'}
msg = ['Updating Attributes clever way:']
updateAttributesClever(objB, dictB, msg)
print '\n\t'.join(msg)
这样做的好处是,每当我需要添加一个新属性时,只需更新要检查的属性列表,其余的代码就已经写好了。那么,怎么用Python的方式来实现这个呢?
4 个回答
1
你可以考虑创建一个函数,这个函数可以接收任意对象,并把名字/值对的字典转换成更有意义的东西。这并不是严格意义上的“Python”策略,但在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
。谢谢大家。有时候你在不同的编程语言之间切换,容易搞混。
2
这个应该适合你:
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
6
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, obj_attribute, adict_value)