ASPN ActiveState Programmer Network  
ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

Title: Automatically initializing instance variables from __init__ parameters
Submitter: Henry Crutcher (other recipes)
Last Updated: 2005/09/21
Version no: 1.2
Category: OOP

 

3 stars 4 vote(s)


Description:

This recipe assigns each parameter to an instance variable of the same name, automating a common pattern of object initialization, and making class definitions more compact.

Source: Text Source

# This code was taken and modified from Alex Martelli's
# "determine the name of the calling function" recipe (Thanks, Alex!)
#
# This code also benefits from a useful enhancement from Gary Robinson, allowing 
# only the arguments to __init__ to be copied, if that is desired.
#
# use sys._getframe() -- it returns a frame object, whose attribute
# f_locals is the list of local variables.  Before any processing goes on,
# will be the list of parameters passed in.
import sys

# By calling sys._getframe(1), you can get this information
# for the *caller* of the current function.  So you can package
# this functionality up into your own handy functions:
def initFromArgs(beingInitted, bJustArgs=False):
    import sys
    codeObject = beingInitted.__class__.__init__.im_func.func_code
    for k,v in sys._getframe(1).f_locals.items():
        if k!='self' and ((not bJustArgs) or k in codeObject.co_varnames[1:codeObject.co_argcount]):
            setattr(beingInitted,k,v)

class Animal:
    def __init__(self,name='Dog',numberOfLegs=4,habitat='Temperate'):
        # any local variables added here will be assigned to the object
        # as if they were parameters
        if name in ('Dog','Cat'):
            pet=True
        initFromArgs(self)
        # modify things here

if __name__ == '__main__':
    dog=Animal()
    octopus = Animal('Octopus',8,'Aquatic')
    print [i.__dict__.items() for i in (dog,octopus)]

Discussion:

Consider the implementation of the Animal class without this recipe.
class Animal:
def __init__(self, name='Dog',numberOfLegs=4,habitat='Temperate'):
self.name=name
self.numberOfLegs=numberOfLegs
self.habitat=habitat
if name in ('Dog','Cat'):
self.pet=True
Notice that the information of what data members are in the Animal class is duplicated, once in the parameter list, and once in the initialization code. Also notice that the "application logic", such as it is, is buried in the initialization code. One could use the Bunch class to store the Animal data, but having the default values presumably provides structure, and illustrates which values are going to be used in "application logic" elsewhere in the program.



Add comment

Number of comments: 7

It's easy enough to do this "by hand", Paul Moore, 2004/07/02
The following shows how:

>>> class C:
...     def __init__(self, a=1, b=2, **kw):
...         c = 3
...         self.__dict__.update(kw)
...         del kw # We don't want this in attrs
...         self.__dict__.update(locals())
...         del self.self # We don't need this either
...     def dump(self):
...         print repr(self.__dict__)
...
>>> c = C(d=4)
>>> c.dump()
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> c.a
1
>>> c.c
3
Note that the commented lines are there purely to tidy up the instance namespace, and if you don't need arbitrary keywords, the **kw stuff can go, leaving just self.__dict__.update(locals()). But idiom or function, I agree, it's something that I need quite often. (And hadn't really thought about the best approach for, until now - so thanks!)
Add comment

Didn't know about locals(), Henry Crutcher, 2004/07/05
THanks! I didn't know about the locals function. That is cool! I just like having it in a function, so I only have to look at one copy...., but that idiom is much cleaner than what was in my code :-).
Add comment

Ignoring locals, Gary Robinson, 2004/07/03
Often, I want to ignore locals when I set attributes. The following modification adds a parm to tell initFromArgs to do that. If bJustArgs is True, only parms to the __init__ method are turned into instance attributes. So, for example, in the Animal class, the "pet" local would be ignored:

def initFromArgs(beingInitted, bJustArgs=True):
    codeObject = beingInitted.__class__.__init__.im_func.func_code
    tupNames = codeObject.co_varnames[1:codeObject.co_argcount]
    for k,v in sys._getframe(1).f_locals.items():
        if (not bJustArgs) or k in codeObject.co_varnames[1:codeObject.co_argcount]:
            setattr(beingInitted,k,v)

Add comment

Thanks for that modification!, Henry Crutcher, 2004/07/05
That is really cool... I'll add that to the recipe... Thanks!
Add comment

use the actual function name of the caller, Peter Schwalm, 2004/07/07
Very nice. As an enhancement I would suggest using the actual name of the caller (instead of the hard-wired name "__init__"). The name of the caller can be found by

    callerName = sys._getframe(1).f_code.co_name
Like your's, this code is also inspired by Alex Martelli (as far as I remember). The code object can then be found by
    codeObject  = beingInitted.__class__.__dict__[callerName].
The full function text would then look like this:
def initFromArgs(beingInitted, bJustArgs = True):
    callerName = sys._getframe(1).f_code.co_name
    codeObject  = beingInitted.__class__.__dict__[callerName].func_code
    for key, value in sys._getframe(1).f_locals.items():
        if (not bJustArgs) or key in codeObject.co_varnames[1:codeObject.co_argcount]:
            setattr(beingInitted, key, value)

Add comment

missing line of code, Martin Miller,Martin Miller, 2005/03/07
The following line is missing in version 1.1 of the code and should be inserted near the beginning of the body of the initFromArgs() function -- i.e. before the for loop where it is used:

    codeObject = beingInitted.__class__.__init__.im_func.func_code

Add comment

Fixed!, Henry Crutcher, 2005/09/21
The new code has that line added back in...
Add comment



Highest rated recipes:

1. A simple XML-RPC server

2. Web service accessible ...

3. a friendly mkdir()

4. SOLVING THE METACLASS ...

5. Povray for python

6. Changing return value ...

7. Implementation of sets ...

8. bag collection class

9. deque collection class

10. Floating Point Simulator




Privacy Policy | Email Opt-out | Feedback | Syndication
© 2006 ActiveState Software Inc. All rights reserved.