A python way to avoid mountainous words if ... else?

This has appeared several times recently, and I would like to deal with it better than I do: I have a number of attributes that I cross-reference between an object and a dictionary. If the value is different from them, I want to set the object.attribute attribute to the dictionary value ['attribute']. I also want to keep track of what has changed.

Now, my first thought is to simply use the if else statement for each attribute, but after writing a few of them, it is obvious that I am rewriting the same code again and again. There should be a DRY way to do this, where I point out only those parts that change every time, and then scroll through all the attributes.

There are 15 different attributes in the production code, but my example below just uses 2 for simplicity. I have some idea on how to do this in a smart way, but I miss the last step, actually setting the object.attribute attribute to the value of the 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) 

The idea is that in this case, when I need to add another attribute, I can just update the list of checked attributes, and the rest of the code is already written. What is the pythonic way to accomplish this?

+4
source share
4 answers

setattr() is what you are looking for:

 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) 
+4
source

This should work for your:

 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 xa print xb 
+2
source

You might want to think about creating a function that can take an arbitrary object and transform a dictionary of name / value pairs into something more meaningful. This is not strictly a "Python" strategy, but something that is pretty easy to do in Python due to support for closures and how it processes objects under the hood:

 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 

I make one assumption, the names in the dictionary always correspond to the variables of the object. If they do not match, you will need to do a mapping.

change

() => [] and object => obj . Thank you, guys. Sometimes you switch from one language to several others, and all this gets confused.

+1
source

Several answers are close, but in order to handle the fact that the key name in the dict does not match the corresponding attribute name of the object, you will need some way to handle this. This can be easily done by adding another dictionary matching the key names in the dict with the attribute names of the object.

 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 
0
source

Source: https://habr.com/ru/post/1347795/


All Articles