2011-04-12 64 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