Is there are good DRY fix for this painful design pattern?

Jugurtha Hadjar jugurtha.hadjar at gmail.com
Tue Feb 27 13:56:50 EST 2018


On 02/26/2018 03:41 PM, 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,
>
>
>

def snatchit(func):
     def snatched(self, *args, **kwargs):
         frame = inspect.signature(func)
         for name in frame.parameters:
             if name != 'self':
                 kwargs.setdefault(name, getattr(self, name))
         return func(self, *args, **kwargs)
     snatched.__name__ = func.__name__
     snatched.__doc__ = func.__doc__
     return snatched


class Foo(object):
     def __init__(self, sleepy, groggy):
         self.sleepy = sleepy
         self.groggy = groggy

     @snatchit
     def spam(self, sleepy=None):
         print("I am spam. sleepy=", sleepy)

     @snatchit
     def ham(self, sleepy=None, groggy=None):
         print("I am ham. sleepy=", sleepy, " and groggy=", groggy)


 >>> f = Foo(1, 19)
 >>> f.spam()
I am spam. sleepy= 1
 >>> f.spam(sleepy=8)
I am spam. sleepy= 8
 >>> f.ham(sleepy=8)
I am ham. sleepy= 8  and groggy= 19
 >>> f.ham(sleepy=17, groggy=888)
I am ham. sleepy= 17  and groggy= 888

-- 
~ Jugurtha Hadjar,




More information about the Python-list mailing list