5

Frequently the constructor of a class will take it's arguments and save them on the instance. For example:

class Example(object):
    def __init__(self, title='',backtitle='', height=20, width=50):
        self.title = title
        self.backtitle = backtitle
        self.height = height
        self.width = width

This is repetitious so I made a helper function to do this automatically:

from inspect import getargspec
def save_args(values):
    for i in getargspec(values['self'].__init__).args[1:]:
        values['self'].__dict__[i] = values[i]

class Example(object):
    def __init__(self, title='',backtitle='', height=20, width=50):
        save_args(vars())

My questions are as follows:

  • Will this fail with certain classes or agruments
  • Is it portable, will it work on Jython, etc.. It worked for me on python 2.7 and 3.2
  • Is there a simpler alternative?
  • Is there a python package out there that already does this?
Marwan Alsabbagh
  • 25,364
  • 9
  • 55
  • 65

2 Answers2

6

It'll fail when your class uses __slots__. You could use setattr() instead:

from inspect import getargspec
def save_args(values):
    for i in getargspec(values['self'].__init__).args[1:]:
        setattr(values['self'], i, values[i])

provided the arguments keyword arguments to __init__ are all declared slots of course.

Otherwise this should work on any Python implementation.

You may be interested in a previous discussion of the topic, which sparked a Python-ideas list thread.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • FGITW -- That's what I get for trying to address each of OP's questions individually when really only the first one was important :) – mgilson Mar 18 '13 at 18:30
  • There was a discussion about this on python-ideas somewhere in the past 6 months, where (after everyone decided nothing had to be added to the language) people proposed and argued about 300 different implementations. It's worth searching the archives if you really care about this. (IIRC, if you don't care about portability, there's a solution using frame hackery that's handles some cases that `getargspec` can't… but I don't remember what cases.) – abarnert Mar 18 '13 at 18:32
  • This might also be neatly handled in a meta-class which wraps the existing `__init__`, although I haven't really fully formed how that would work in my head (mostly because I'm pretty inexperienced with metaclasses) – mgilson Mar 18 '13 at 18:33
  • 1
    @mgilson: a decorator on a `__init__` method with just a `pass` body would do too. – Martijn Pieters Mar 18 '13 at 18:37
  • Yeah, that's true too. I do have some experience with decorators :) – mgilson Mar 18 '13 at 18:39
  • @abarnert: Thanks for that pointer; found the thread at http://mail.python.org/pipermail/python-ideas/2011-April/thread.html#9930, which was triggered by [Python: Automatically initialize instance variables?](http://stackoverflow.com/q/1389180) actually. – Martijn Pieters Mar 18 '13 at 18:43
  • @MartijnPieters: I'm pretty sure there was a more recent thread referencing that one. (And I'll bet there's similar threads every 12-18 months going back for years…) – abarnert Mar 18 '13 at 18:55
1

This requires you to write more code, silently ignores all erroneous arguments to the Example constructor, and doesn't support positional arguments to the Example constructor, but avoids use of inspect:

def save_args(obj, defaults, kwargs):
    for k,v in defaults.iteritems():
        if k in kwargs: v = kwargs[k]
        setattr(obj, k, v)

class Example(object):
    def __init__(self, **kwargs):
        defaults = { 'title': '',
                     'backtitle': '',
                     'height': 20,
                     'width': 50 }
        save_args(self, defaults, kwargs)
zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    What's wrong with `inspect`? As long as the ugly is sufficiently hidden, I don't see any problem with it :) – mgilson Mar 18 '13 at 18:40
  • Nothing's wrong with `inspect`, I just wanted to demonstrate that it could be done without. – zwol Mar 18 '13 at 18:46