Is there are good DRY fix for this painful design pattern?
marco.nawijn at colosso.nl
marco.nawijn at colosso.nl
Mon Feb 26 15:09:32 EST 2018
On Monday, February 26, 2018 at 3:44:14 PM UTC+1, 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
What about something along these lines:
from inspect import getargspec, getargvalues
class Demo(object):
def __init__(self, a=None, b=10, c='oops'):
spec = getargspec(self.__init__)
for key, value in zip(spec.args[1:], spec.defaults):
setattr(self, key, value)
def foo(self, *args, **kwargs):
assert len(args) == 0
for k, v in kwargs.items():
try:
getattr(self, k)
except AttributeError:
print('*** error: attribute {} does not exist.'.format(k))
setattr(self, k, v)
d = Demo()
print(d.a)
d.foo(a=3.14)
print(d.a)
d.foo(q='this will raise')
So, use the inspect module to detect the valid arguments
from the class initializer. Then use **kwargs in every
class method. It would be nice if the full method signature
can be kept, but I have not yet figured out if this is
possible.
Marco
More information about the Python-list
mailing list