Automatically initialize instance variables?

Solution 1:

You can use a decorator:

from functools import wraps
import inspect

def initializer(func):
    """
    Automatically assigns the parameters.

    >>> class process:
    ...     @initializer
    ...     def __init__(self, cmd, reachable=False, user='root'):
    ...         pass
    >>> p = process('halt', True)
    >>> p.cmd, p.reachable, p.user
    ('halt', True, 'root')
    """
    names, varargs, keywords, defaults = inspect.getargspec(func)

    @wraps(func)
    def wrapper(self, *args, **kargs):
        for name, arg in list(zip(names[1:], args)) + list(kargs.items()):
            setattr(self, name, arg)

        for name, default in zip(reversed(names), reversed(defaults)):
            if not hasattr(self, name):
                setattr(self, name, default)

        func(self, *args, **kargs)

    return wrapper

Use it to decorate the __init__ method:

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'

Solution 2:

For Python 3.7+ you can use a Data Class, which is a very pythonic and maintainable way to do what you want.

It allows you to define fields for your class, which are your automatically initialized instance variables.

It would look something like that:

@dataclass
class Process:
    PID: int
    PPID: int
    cmd: str
    ...

The __init__method will already be in your class.

Note that here type hinting is required, that is why I have used int and str in the example. If you don't know the type of your field, you can use Any from the typing module.

The Data Class has many advantages compared to the proposed solutions:

  • It is explicit: all fields are visible, which respects the Zen of Python and makes it readable and maintainable. Compare it to the use of **kwargs.
  • It can have methods. Just like any other class.
  • It allows you to go beyond the automatic __init__ using the __post_init__ method.

EDIT: Reasons to avoid using NamedTuples

Some suggest the use of namedtuple for this case, but namedtuples have some behaviours that differs from Python classes, which are not really evident at first and should be well known:

1. NamedTuples are immutable

Immutability can be useful, but maybe it is not what you want for your instances. DataClasses can also be somehow immutable if you use the argument frozen=True on the @dataclass decorator.

2. NamedTuples __eq__ behaves like Tuple's

In Python, SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2) is True! The __eq__ function of NamedTuple, used in comparisons, only considers the values and the positions of those values on the compared instances, not their class or fields' names.

Solution 3:

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.

Solution 4:

Quoting the Zen of Python,

Explicit is better than implicit.

Solution 5:

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