Recipe6.18.Automatically Initializing Instance Variables from _ _init_ _ Arguments


Recipe 6.18. Automatically Initializing Instance Variables from _ _init_ _ Arguments

Credit: Peter Otten, Gary Robinson, Henry Crutcher, Paul Moore, Peter Schwalm, Holger Krekel

Problem

You want to avoid writing and maintaining _ _init_ _ methods that consist of almost nothing but a series of self.something = something assignments.

Solution

You can "factor out" the attribute-assignment task to an auxiliary function:

def attributesFromDict(d):     self = d.pop('self')     for n, v in d.iteritems( ):         setattr(self, n, v)

Now, the typical boilerplate code for an _ _init_ _ method such as:

    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

can become a short, crystal-clear one-liner:

    def _ _init_ _(self, foo, bar, baz, boom=1, bang=2):         attributesFromDict(locals( ))

Discussion

As long as no additional logic is in the body of _ _init_ _, the dict returned by calling the built-in function locals contains only the arguments that were passed to _ _init_ _ (plus those arguments that were not passed but have default values). Function attributesFromDict extracts the object, relying on the convention that the object is always an argument named 'self', and then interprets all other items in the dictionary as names and values of attributes to set. A similar but simpler technique, not requiring an auxiliary function, is:

    def _ _init_ _(self, foo, bar, baz, boom=1, bang=2):          self._ _dict_ _.update(locals( ))          del self.self

However, this latter technique has a serious defect when compared to the one presented in this recipe's Solution: by setting attributes directly into self._ _dict_ _ (through the latter's update method), it does not play well with properties and other advanced descriptors, while the approach in this recipe's Solution, using built-in setattr, is impeccable in this respect.

attributesFromDict is not meant for use in an _ _init_ _ method that contains more code, and specifically one that uses some local variables, because attributesFromDict cannot easily distinguish, in the dictionary that is passed as its only argument d, between arguments of _ _init_ _ and other local variables of _ _init_ _. If you're willing to insert a little introspection in the auxiliary function, this limitation may be overcome:

def attributesFromArguments(d):     self = d.pop('self')     codeObject = self._ _init_ _.im_func.func_code     argumentNames = codeObject.co_varnames[1:codeObject.co_argcount]     for n in argumentNames:         setattr(self, n, d[n])

By extracting the code object of the _ _init_ _ method, function attributesFromArguments is able to limit itself to the names of _ _init_ _'s arguments. Your _ _init_ _ method can then call attributesFromArguments(locals( )), instead of attributesFromDict(locals( )), if and when it needs to continue, after the call, with more code that may define other local variables.

The key limitation of attributesFromArguments is that it does not support _ _init_ _ having a last special argument of the **kw kind. Such support can be added, with yet more introspection, but it would require more black magic and complication than the functionality is probably worth. If you nevertheless want to explore this possibility, you can use the inspect module of the standard library, rather than the roll-your-own approach used in function attributeFromArguments, for introspection purposes. inspect.getargspec(self._ _init_ _) gives you both the argument names and the indication of whether self._ _init_ _ accepts a **kw form. See Recipe 6.19 for more information about function inspect.getargspec. Remember the golden rule of Python programming: "Let the standard library do it!"

See Also

Library Reference and Python in a Nutshell docs for the built-in function locals, methods of type dict, special method _ _init_ _, and introspection techniques (including module inspect).



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net