How do I avoid the "self.x = x; self.y = y; self.z = z" pattern in __init__? How do I avoid the "self.x = x; self.y = y; self.z = z" pattern in __init__? python python

How do I avoid the "self.x = x; self.y = y; self.z = z" pattern in __init__?


Disclaimer: It seems that several people are concerned about presenting this solution, so I will provide a very clear disclaimer. You should not use this solution. I only provide it as information, so you know that the language is capable of this. The rest of the answer is just showing language capabilities, not endorsing using them in this way.


There isn't really anything wrong with explicitly copying parameters into attributes. If you have too many parameters in the ctor, it is sometimes considered a code smell and maybe you should group these params into a fewer objects. Other times, it is necessary and there is nothing wrong with it. Anyway, doing it explicitly is the way to go.

However, since you are asking HOW it can be done (and not whether it should be done), then one solution is this:

class A:    def __init__(self, **kwargs):        for key in kwargs:          setattr(self, key, kwargs[key])a = A(l=1, d=2)a.l # will return 1a.d # will return 2


Edit:If you have python 3.7+ just use dataclasses

A decorator solution that keeps the signature:

import decoratorimport inspectimport sys@decorator.decoratordef simple_init(func, self, *args, **kws):    """    @simple_init    def __init__(self,a,b,...,z)        dosomething()    behaves like    def __init__(self,a,b,...,z)        self.a = a        self.b = b        ...        self.z = z        dosomething()    """    #init_argumentnames_without_self = ['a','b',...,'z']    if sys.version_info.major == 2:        init_argumentnames_without_self = inspect.getargspec(func).args[1:]    else:        init_argumentnames_without_self = tuple(inspect.signature(func).parameters.keys())[1:]    positional_values = args    keyword_values_in_correct_order = tuple(kws[key] for key in init_argumentnames_without_self if key in kws)    attribute_values = positional_values + keyword_values_in_correct_order    for attribute_name,attribute_value in zip(init_argumentnames_without_self,attribute_values):        setattr(self,attribute_name,attribute_value)    # call the original __init__    func(self, *args, **kws)class Test():    @simple_init    def __init__(self,a,b,c,d=4):        print(self.a,self.b,self.c,self.d)#prints 1 3 2 4t = Test(1,c=2,b=3)#keeps signature#prints ['self', 'a', 'b', 'c', 'd']if sys.version_info.major == 2:    print(inspect.getargspec(Test.__init__).args)else:    print(inspect.signature(Test.__init__))


explicit is better than implicit ... so sure you could make it more concise:

def __init__(self,a,b,c):    for k,v in locals().items():        if k != "self":             setattr(self,k,v)

The better question is should you?

... that said if you want a named tuple I would recommend using a namedtuple (remember tuples have certain conditions attached to them) ... perhaps you want an ordereddict or even just a dict ...