ActiveState Code

Recipe 280381: Clean up __init__() methods that contain only attribute assignments.


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.

Python
 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())

Discussion

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

Comments

  1. 1. At 11:39 a.m. on 28 apr 2004, Gary Robinson said:

    A more robust approach? The following version works even in __init__ methods which have other logic and temporary variables:

    def assignAttr(obj, dctLocals):
        codeObject = obj.__class__.__init__.im_func.func_code
        tupNames = codeObject.co_varnames[1:codeObject.co_argcount]
        for strName in tupNames:
           obj.__dict__[strName] = dctLocals[strName]
    
    class  After:
        def __init__(self, foo, bar, baz, boom=1, bang=2):
            assignAttr(self, locals())
    
  2. 2. At 7:55 a.m. on 1 sep 2006, Alain Pointdexter said:

    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()

Sign in to comment