Did you ever have to write an initializer whose body consisted of little more than a suite of self.attr = attr style assignments? Here's a utility function that factors out this task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def attributesFromDict(d, obj=None, objName="self"):
if obj is None:
obj = d.pop(objName)
for n, v in d.iteritems():
setattr(obj, n, v)
class Before:
def __init__(self, foo, bar, baz, boom=1, bang=2):
self.foo = foo
self.bar = bar
self.baz = baz
self.boom = boom
self.bang = bang
class After:
def __init__(self, foo, bar, baz, boom=1, bang=2):
attributesFromDict(locals())
|
As there is no additional logic in After.__init__(), the locals() dictionary contains only the parameters provided. attributesFromDict() by default extracts self and interprets all other items in the dictionary as attribute name/value pairs which are then set. This is similar to the technique of using only keyword parameters
def __init__(self, **kw): self.__dict__.update(kw)
but the latter lacks an easy way to specify the mandatory/optional parameters and has often proved a showstopper when reading other people's code.
Limitations: As there is no easy way to specify a subset of the local variables, you cannot conveniently use attributesFromDict() in a method with complex logic that needs temporary variables. For the same reason it is not easily applicable in a subclass, that propagates a subset of its parameters to the baseclass initializer.
Credits: John J. Lee originally posted the problem on comp.lang.python, and if you are interested in the strange detours that led to the solution presented above, have a look at http://mail.python.org/pipermail/python-list/2004-April/216871.html
A more robust approach? The following version works even in __init__ methods which have other logic and temporary variables:
a simplification (no parameter needed). import inspect
def init_attributes():
......d=inspect.currentframe().f_back.f_locals
......obj = d.pop("self")
......for n, v in d.iteritems():
............setattr(obj, n, v)
class After:
......def __init__(self, foo, bar, baz, boom=4, bang=5):
...........init_attributes()
Note that init_attributes() relies on the fact that the first method argument is called "self". While that's true in virtually all code I've seen or written, it's still a dependency. (Also, init_attributes() needs to be called before any local variables are assigned in __init__().)
For anyone who may be care about those admittedly very small issues, here's a version of assignAttr that still takes self, but doesn't need locals() to be passed in (it uses the inspect module as taught by init_attributes()):
def assignAttr(self): import inspect name2Var = d=inspect.currentframe().f_back.f_locals codeObject = self.__class__.__init__.im_func.func_code tupNames = codeObject.co_varnames[1:codeObject.co_argcount] for name in tupNames: setattr(self, name, name2Var[name])