2011-04-12 15 views
4

これは最近、何度か登場しています。私はこれまでよりもうまく対処したいと思います。オブジェクトと辞書を相互参照する一連の属性があります。値が異なる場合、object.attributeをdictionary ['attribute']値に設定します。私はまた何が変わっているのかを把握したい。if ... else文の山を避けるPythonの方法?

私の最初の考えは、すべての属性に対して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, 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の方法は何ですか?

+0

これをすべて関数/関数にマージできます。それはあなたのコードをより綺麗に見せ、あなたの人生を楽にします。 – iamandrus

+0

それは私の側で機能している - 私は例を単純化しようとしていた。 – Nathan

答えて

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

1

任意のオブジェクトをとり、名前/値のペアの辞書をより意味のあるものに変換できる関数を作成することを考えたいと思うかもしれません。これは、厳密に、そのクロージャのサポートと、それはボンネットの下にオブジェクトをどのように扱うかの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 

私は1つの仮定を作っています、中の名前辞書は常にオブジェクト変数に対応します。それらが同じでない場合は、マッピングを行う必要があります。

編集

() =>[]object =>obj。みんなありがとう。時々あなたはある言語からいくつかの言語に行き、それはすべて混乱します。

+1

私はあなたが 'object .__ dict __(name)'の代わりに 'object .__ dict__ [name]'を意味すると思います。変数名に 'object'を使うべきでもないのは、組み込み関数の名前でもあるからです。 ;) –

+0

デコレータのように見えます。だから私は 'checkUpdates(obj)'で呼び出すと、辞書とすべての値をループする関数であるupdatedを返します。コードは 'func = checkUpdates(objA)'、 'func(dictA)'でしょうか?私はそれを試してみましょう... – Nathan

+0

@caribou:別の関数を作成して返す関数ですが、デコレータではありません。それを使用する方法についてのあなたの推測はほぼ正しいですが、 'func'に' msg'引数を渡す必要もあります。 – martineau

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

回答のカップルが接近しているが、名という事実を処理するために、 dictのキーが対応するオブジェクトの属性名と一致しない場合は、それを処理する方法が必要です。これは、辞書内のキーの名前をオブジェクトの属性の名前にマッピングするさらに別の辞書を追加することで簡単に行うことができます。

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