Code refactoring with python decorators?
decorator, python, refactoring
Solution
What you are describing is a situation where you have some boilerplate, some behaviour, followed by some boiler plate. Essentially a situation where you could use a Higher Order Function (like map, reduce or filter).
You could do what Ned suggests (though, I'd use functools.partial rather than defining fooA/fooB longhand):
import functools
...
fooA = functools.partial(call_one, _fooA)
fooB = functools.partial(call_one, _fooB)
... but that effectively gets you back to the same place as with your decorator, introducing some clutter into the namespace along the way.
You could rewrite your decorator to allow functions that only take one parameter, but return functions that take two:
def refactorMe(func):
def wrapper(parm1, parm2):
if parm1:
code_chunk_1
func(parm1)
if parm2[-1]:
code_chunk_2
return wrapper
Getting rid of the star magic is an improvement as this decorator is not general to all functions so we should be explicit about it. I like the fact that we change the number of parameters less as anyone looking at the code could easily be confused by the fact that when we call the function we are adding an extra parameter. Furthermore it just feels like decorators that change the signature of the function they decorate should be bad form.
In summary:
Decorators are higher order functions, and templating behaviour is precisely what they're for.
I would embrace the fact that this code is specific to your fooXXX functions, by making the decorator internal and having it take precisely the number of arguments needed (because foo(*args, **kwargs) signatures makes introspection a pain).
def _refactorMe(func):
@functools.wraps(func) #the wraps decorator propagates name/docsting
def wrapper(parm1, parm2):
if parm1:
code_chunk_1
func(parm1, parm2)
if parm2:
code_chunk_2
return wrapper
I'd leave the calls taking two parameters, even though one is unused just so that the decorator doesn't change the signature. This isn't strictly necessary as if you document the functions as they look after decoration and you are restricting the use of the decorator to this small set of functions then the fact that the signature changes shouldn't be that big a deal.
@_refactorMe
def fooB(param1, param2):
fooB_code #uses only param1
@_refactorMe
def fooB(param1, param2):
fooB_code #uses only param1
Problem
I'm actually struggling with some piece of code. I do know that it can be refactored, but I can't find the nice-smart-elegant solution. Here are two functions (much more functions of that kind are in my code): ``` def fooA(param1, param2): if param2 == True: code_chunk_1 fooA_code #uses only param1 if param2 == True: code_chunk_2 def fooB(param1, param2): if param2 == True: code_chunk_1 fooB_code #uses only param1 if param2 == True: code_chunk_2 ``` My first idea was to use this decorator: ``` def refactorMe(func): def wrapper(*args): if args[-1]: code_chunk_1 func(*args) if args[-1]: code_chunk_2 return wrapper ``` And finally: ``` @refactorMe def fooA(param1, param2): fooA_code #uses only param1 @refactorMe def fooB(param1, param2): fooB_code #uses only param1 ``` Unfortunately, I'm not happy with this solution: - This decorator is "intrusive" and specific to the fooA & fooB functions - param2 is not used anymore in the fooA & fooB body, but we must keep it in the function signature Perhaps I'm not using the decorator for its initial purpose? Is there any other way to refactor the code? Thanks a lot!