Automatically initialize instance variables?
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'
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.