views:

725

answers:

6

Hi,

I have a python class that looks like this:

class process:
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):

followed by:

        self.PID=PID
        self.PPID=PPID
        self.cmd=cmd
        ...

Is there any way to autoinitialize these instance variables, like C++'s initialization list? It would spare lots of redundant code.

+4  A: 

You could do it easily with the keyword arguments, e.g. like this:

>>> class D:
    def __init__(self, **kwargs):
     for k, v in kwargs.items():
      setattr(self, k, v)

>>> D(test='d').test
'd'

similar implementation for the positional arguments would be:

>> class C:
    def __init__(self, *args):
     self.t, self.d = args


>>> C('abc', 'def').t
'abc'
>>> C('abc', 'def').d
'def'

which to me doesn't seem to solve your problem.

SilentGhost
Another variation that I like is `self.__dict__.update( **kwargs )`
S.Lott
Might as well use locals() and put normal arguments.
Mk12
+20  A: 

Edit: Adam asked me to extend the solution to support keyword arguments

Here is the final solution:

from functools import wraps
import inspect

def initializer(fun):
   names, varargs, keywords, defaults = inspect.getargspec(fun)
   @wraps(fun)
   def wrapper(self, *args, **kargs):
       for name, arg in zip(names[1:], args) + kargs.items():
           setattr(self, name, arg)
       fun(self, *args, **kargs)
   return wrapper

You can use a decorator:

from functools import wraps
import inspect

def initializer(fun):
    names, varargs, keywords, defaults = inspect.getargspec(fun)
    @wraps(fun)
    def wrapper(self, *args):
        for name, arg in zip(names[1:], args):
            setattr(self, name, arg)
        fun(self, *args)
    return wrapper

class process:
    @initializer
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
        pass

Output:

>>> c = process(1, 2, 3, 4, 5, 6)
>>> c.PID
1
>>> dir(c)
['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
Nadia Alramli
minor nitpick - you forgot to import inspect
John Montgomery
@John, thanks.
Nadia Alramli
This works and answer the question so I voted up. But I kept Ferdidand Beyer answer: "Explicit is better than implicit"
unkiwii
+1 For great answer that solved my problem. But shouldn't it be a core functionality of the language? Do you think it's worth writing a PEP?
Adam Matan
@Nadia nice, but inside the wrapper fun should also be called, in case its body is more than just 'pass' -- no downsides to that! So pls edit this good answer to make it complete.
Alex Martelli
@Udi, a PEP can't hurt, but you'll get lots of push-back (see e.g. Ferdinand Beyer's answer below). Then again, these days you ALWAYS get lots of push-back for ANY pep at all;-).
Alex Martelli
@Alex, you are right. I fixed it. Thanks!
Nadia Alramli
This is a really good answer - this has gone straight into my toolbox.
Michael van der Westhuizen
Thanks for this, it was pretty much exactly what I was looking for. Note that it won't work if you use keyword arguments or use default values, though you can pretty easily modify it to handle that case.
Paul D.
+8  A: 

Quoting the Zen of Python,

Explicit is better than implicit.

Ferdinand Beyer
Wouldn't an initialization list declaration be explicit enough?
Adam Matan
I guess. But I don't see a reason for adding something like that to the language. I clearly prefer multiple assignment statements over some kind of decorator-magic behind the scenes.
Ferdinand Beyer
@Ferdinand, I agree it would be silly to have in the language something that can perfectly well be in the stdlib, but, it SHOULD be in the stdlib, because "beautiful is better than ugly" takes precedence, and many repetitive assignments are ugly (as is any form of repetition).
Alex Martelli
+7  A: 

If you're using Python 2.6 or higher, you can use collections.namedtuple:

>>> from collections import namedtuple
>>> Process = namedtuple('Process', 'PID PPID cmd')
>>> proc = Process(1, 2, 3)
>>> proc.PID
1
>>> proc.PPID
2

This is appropriate especially when your class is really just a big bag of values.

Kiv
+1 for really neat tool.
Adam Matan
+4  A: 

Another thing you can do:

class X(object):
    def __init__(self, a,b,c,d):
        vars = locals() # dict of local names
        self.__dict__.update(vars) # __dict__ holds and object's attributes
        del self.__dict__["self"] # don't need `self`

But the only solution I would recommend, besides just spelling it out, is "make a macro in your editor" ;-p

THC4k
+2  A: 

Nadia's solution is better and more powerful, but I think this is also interesting:

def constructor(*arg_names):
  def __init__(self, *args):
    for name, val in zip(arg_names, args):
      self.__setattr__(name, val)
  return __init__


class MyClass(object):
  __init__ = constructor("var1", "var2", "var3")


>>> c = MyClass("fish", "cheese", "beans")
>>> c.var2
"cheese"
Andrew Magee