Something like the following might do the trick. As an added benefit, it's easy to set all the defaults automatically in __init__ as well without hand-adding "self.dopey = dopey". On the down side, in the non-__init__ functions, you have to use kwargs["dopey"] and the like. It also involves tacking an "__init_args" onto your object so that the decorator knows what was passed to the __init__ function.
-tkc from functools import wraps from inspect import getargspec def template(original_init_fn): args, varargs, keywords, defaults = getargspec(original_init_fn) assert varargs is keywords is None arg_dict = dict(zip(args[-len(defaults):], defaults)) @wraps(original_init_fn) def new_init_fn(self, *args, **kwargs): self.__init_args = arg_dict.copy() self.__init_args.update(kwargs) # if you don't want to automatically set attributes # remove these next two lines for k, v in self.__init_args.items(): setattr(self, k, v) return original_init_fn(self, *args, **kwargs) def templatify(fn): @wraps(fn) def new_templated_fn(self, *args, **kwargs): for k, v in self.__init_args.items(): if k not in kwargs: kwargs[k] = v return fn(self, *args, **kwargs) return new_templated_fn new_init_fn.templatify = templatify return new_init_fn class Foo: @template def __init__(self, bashful=None, dopey=None, doc="On definition", ): pass # look, ma, no manual assignment! @__init__.templatify def myfunc(self, **kwargs): print(kwargs) f1 = Foo() f2 = Foo(bashful="on init", dopey="on init") for fn in (f1, f2): fn.myfunc() fn.myfunc(bashful="on myfunc") On 2018-02-26 14:41, Steven D'Aprano wrote: > I have a class with a large number of parameters (about ten) > assigned in `__init__`. The class then has a number of methods > which accept *optional* arguments with the same names as the > constructor/initialiser parameters. If those arguments are None, > the defaults are taken from the instance attributes. > > An example might be something like this: > > > class Foo: > def __init__(self, bashful, doc, dopey, grumpy, > happy, sleepy, sneezy): > self.bashful = bashful # etc > > def spam(self, bashful=None, doc=None, dopey=None, > grumpy=None, happy=None, sleepy=None, > sneezy=None): > if bashful is None: > bashful = self.bashful > if doc is None: > doc = self.doc > if dopey is None: > dopey = self.dopey > if grumpy is None: > grumpy = self.grumpy > if happy is None: > happy = self.happy > if sleepy is None: > sleepy = self.sleepy > if sneezy is None: > sneezy = self.sneezy > # now do the real work... > > def eggs(self, bashful=None, # etc... > ): > if bashful is None: > bashful = self.bashful > # and so on > > > There's a lot of tedious boilerplate repetition in this, and to add > insult to injury the class is still under active development with > an unstable API, so every time I change one of the parameters, or > add a new one, I have to change it in over a dozen places. > > Is there a good fix for this to reduce the amount of boilerplate? > > > Thanks, > > > > -- > Steve > > -- > https://mail.python.org/mailman/listinfo/python-list -- https://mail.python.org/mailman/listinfo/python-list