How do I DRY the following code?

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Tue Dec 30 03:52:05 EST 2008


On Mon, 29 Dec 2008 21:13:55 -0500, R. Bernstein wrote:

> How do I DRY the following code? 
>
> class C():
[snip code]

Move the common stuff into methods (or possibly external functions). If 
you really need to, make them private by appending an underscore to the 
front of the name.


class C():
  def common1(self, *args):
    return "common1"
  def common2(self, *args):
    return "common2"
  def _more(self, *args):  # Private, don't touch!
    return "more stuff"

  def f1(self, arg1, arg2=None, globals=None, locals=None):
      ... unique stuff #1 ...
      self.common1()
      ret = eval(args, globals, locals)
      self._more()
      return retval

  def f2(self, arg1, arg2=None, *args, **kwds):
      ... unique stuff #2 ...
      self.common1()
      ret = arg1(args, *args, **kwds)
      self.common2
      return retval

  def f3(self, arg1, globals=None, locals=None):
      ... unique stuff #3 ...
      self.common1()
      exec cmd in globals, locals
      self.common2()
      return None  # unneeded

  def f4(self, arg1, globals=None, locals=None):
      ... unique stuff #4 ...
      self.common1()
      execfile(args, globals, locals)
      self._more()


An explicit "return None" is probably not needed. Python functions and 
methods automatically return None if they reach the end of the function.





> Above there are two kinds of duplication: that within class C and that
> outside which creates an instance of the class C and calls the
> corresponding method.

Do you really need them? If so, you're repeating yourself by definition. 
That's not necessarily a problem, the stand-alone functions are just 
wrappers of methods. You can decrease (but not eliminate) the amount of 
repeated code with a factory function:

def build_standalone(methodname, docstring=None):
    def function(arg, arg2=None, globals=None, locals=None):
        c = C()
        return c.getattr(methodname)(arg, arg2, globals, locals)
    function.__name__ = methodname
    function.__doc__ = docstring
    return function

f1 = build_standalone('f1', "Docstring f1")
f2 = build_standalone('f2', "Docstring f2")
f3 = build_standalone('f3', "Docstring f3")

There's no way to avoid the repeated f1 etc.

But honestly, with only three such functions, I'd consider that overkill.


> Lest the above be too abstract, those who want to look at the full
> (and redundant) code:
>
>   http://code.google.com/p/pydbg/source/browse/trunk/api/pydbg/api/
debugger.py


You have parameters called Globals and Locals. It's the usual Python 
convention that names starting with a leading uppercase letter is a 
class. To avoid shadowing the built-ins, it would be more conventional to 
write them as globals_ and locals_. You may or may not care about 
following the convention :)

I notice you have code like the following:

if Globals is None:
    import __main__
    Globals = __main__.__dict__


I would have written that like:

if Globals is None:
    Globals = globals()

or even

if Globals is None:
    from __main__ import __dict__ as Globals

You also have at least one unnecessary pass statement:

if not isinstance(expr, types.CodeType):
    expr = expr+'\n'
    pass

The pass isn't needed.


In your runcall() method, you say:

res = None
self.start(start_opts)
try:
    res = func(*args, **kwds)
except DebuggerQuit:
    pass
finally:
    self.stop()
return res

This is probably better written as:

self.start(start_opts)
try:
    return func(*args, **kwds)
except DebuggerQuit:
    return None
finally:
    self.stop()




-- 
Steven



More information about the Python-list mailing list