Is there are good DRY fix for this painful design pattern?
Tim Chase
python.list at tim.thechases.com
Tue Feb 27 12:47:13 EST 2018
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
More information about the Python-list
mailing list