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

Rob Gaddi rgaddi at highlandtechnology.invalid
Mon Feb 26 11:30:35 EST 2018


On 02/26/2018 07:28 AM, Rhodri James wrote:
> On 26/02/18 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?
> 
> You could use dicts?  Untested code:
> 
> class Foo:
>      def __init__(self, **kwds):
>          self.defaults = kwds
> 
>      def spam(self, **kwds):
>          vars = self.defaults.copy()
>          vars.update(kwds)
>      # Do the work with vars['bashful'] etc
> 
> 

That's what I was about to suggest (and equally fail to test).  It at 
least segregates the set of "instance variables I want to have 
auto-default behavior" from the rest of them.  Yeah, **kwargs is clumsy, 
but it gets the job done.

-- 
Rob Gaddi, Highland Technology -- www.highlandtechnology.com
Email address domain is currently out of order.  See above to fix.



More information about the Python-list mailing list