On Wed, Sep 28, 2016 at 1:49 AM, Steve D'Aprano <steve+pyt...@pearwood.info> wrote: > @classmethod > def from_strings(cls, bashful='10.0', doc='20.0', dopey='30.0', > grumpy='40', happy='50', sleepy='60', sneezy='70'): > bashful = float(bashful) > doc = float(doc) > dopey = float(dopey) > grumpy = int(grumpy) > happy = int(happy) > sleepy = int(sleepy) > sneezy = int(sneezy) > return cls(bashful, doc, dopey, grumpy, happy, sleepy, sneezy) > > > That's a pretty ugly DRY violation. Imagine that I change the default value > for bashful from 10.0 to (let's say) 99. I have to touch the code in three > places (to say nothing of unit tests): > > - modify the default value in __init__ > - modify the stringified default value in from_strings > - change the conversion function from float to int in from_strings > > > Not to mention that each parameter is named seven times. >
You could go data-driven. Assuming that all your default values are in the appropriate type (eg you use 10.0 rather than 10, when you want a float), you could use those directly. class Spam: def __init__(self, bashful=10.0, doc=20.0, dopey=30.0, grumpy=40, happy=50, sleepy=60, sneezy=70): for name, default in zip(self.__init__.__defaults__, self.__init__.__code__.co_varnames[1:]): setattr(self, name, type(default)(locals()[name])) Your basic __init__ method is now capable of handling strings as well, so from_strings can simply construct the object directly. I'm not sure what the advantage of from_strings is, but assuming you still need it, you could write it thus: @classmethod def from_strings(cls, bashful, doc, dopey, grumpy, happy, sleepy, sneezy): return cls(bashful, doc, dopey, grumpy, happy, sleepy, sneezy) from_strings.__func__.__defaults__ = tuple(str(x) for x in __init__.__defaults__) No duplication of type names or default values, though there is still duplication of parameter names. You could eliminate that by going *args,**kw, but at the expense of introspectability. Actually, you could probably just use wraps... @classmethod @functools.wraps(__init__, assigned=()) def from_strings(cls, *a, **kw): return cls(*a, **kw) from_strings.__func__.__defaults__ = tuple(str(x) for x in __init__.__defaults__) Though I'm not particularly enamoured of this way of doing it. ChrisA -- https://mail.python.org/mailman/listinfo/python-list