2011-04-12 56 views
4

最近出现了好几次,我想比它更好地处理它:我有一系列属性可以在对象和字典之间交叉引用。如果它们之间的值不同,我想将object.attribute设置为dictionary ['attribute']的值。我也想跟踪发生了什么变化。Pythonic的方式,以避免山如果...其他语句?

现在,我的第一个想法是只对每个属性使用if else语句,但在编写其中的一些内容后,很明显我会重复编写相同的代码。必须有一种干式的方法来做到这一点,在那里我只指定每次都在变化的部分,然后遍历所有的属性。

在生产代码中,有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, 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) 

的想法是这样,每当我需要添加另一个属性,我可以更新被检查的属性列表和其余代码已经编写完成。 Pythonic的方法是什么?

+0

您可以将所有这些合并到一个函数中。它会让你的代码看起来更整洁,让你的生活更轻松。 – iamandrus 2011-04-12 19:46:51

+0

它在我身边的功能 - 我只是试图简化这个例子。 – Nathan 2011-04-12 21:27:59

回答

4

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) 
+0

谢谢! setattr()正是我所期待的。我还应该记住在使用for循环时要更好地解开元组,这也是一种感谢。 – Nathan 2011-04-12 21:21:42

1

您可能想要考虑创建一个可以接受任意对象并将名称/值对字典转换为更有意义的函数的函数。这不是严格意义上的“巨蟒”的策略,但东西是很容易在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。多谢你们。有时你从一种语言转到另外一种语言,这一切都变得混乱。

+1

我想你是指'object .__ dict __ [name]'而不是'object .__ dict __(name)'。你也不应该使用'object'作为变量名称,因为它也是内建的名称。 ;) – 2011-04-12 19:52:11

+0

看起来像一个装饰者。所以我用'checkUpdates(obj)'调用它,并且它返回更新,这是一个函数,它接受字典并遍历所有值。所以代码将是'func = checkUpdates(objA)'然后'func(dictA)'?让我试试看... – Nathan 2011-04-12 20:00:05

+0

@caribou:虽然它是创建并返回另一个函数的函数,但它不是装饰器。你对如何使用它的猜测几乎是正确的,但你还需要传递'func'和'msg'参数。 – martineau 2011-04-12 20:36:03

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 
0

一对夫妇的答案是接近,但处理这一事实,的名称键的字典不匹配相应的对象的属性名称,你需要一些方法来处理。这可以通过添加另一个字典来映射对象属性的名称来轻松完成。

class someClass: 
    def __init__(self, name, 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