My question refers to organizing code that is needed several times inside a function, and in no other place.
Assuming the following use case:
class A(object):
def __init__(self, base):
self.base = base
def foo(self):
result = self.base + 2 # Should go to an extra function.
result = result * 4
if result > 10:
result = result + 2 # Should go to an extra function.
return result
def bar(self):
pass
There is a repetition in foo()
with the x + 2
part, which in my project are 20 lines of code (adding values to 20 attributes of another object) that should be abstracted. But where to put those? I see three ways to do it:
(1.) A nested function:
class A(object):
# ...
def foo(self):
def plus_two(value):
return value + 2
result = plus_two(self.base)
result = result * 4
if result > 10:
result = plus_two(result)
return result
# ...
This seems to make sense because it is a very specific use case that is only related to what happens inside the method.
But: It can't be tested, the nested function can't be accessed from outside for unit tests. And I really don't want to test this as part of foo()
, as it would require to test all of plus_two
twice (for both if cases). In unit tests, it should be possible to test plus_two
separately, and only its correct invocation in foo()
.
(2.) A helper method:
class A(object):
# ...
def foo(self):
result = self.plus_two(self.base)
result = result * 4
if result > 10:
result = self.plus_two(result)
return result
def plus_two(self, value):
return value + 2
# ...
But: That method is and will never be used by any other method in the class, neither requires access of self
, so it should not become a method of that class. It is not pythonic to collect functions in classes that don't require access to the object, or don't need to be overwritten as part of an interface.
(3.) A module function:
def plus_two(value):
return value + 2
class A(object):
# ...
def foo(self):
result = plus_two(self.base)
result = result * 4
if result > 10:
result = plus_two(result)
return result
# ...
But: This results in several helper functions taken out of its very specific context, in other words, does not follow encapsulation. While this doesn't seem to be a problem here and might appear to be the solution, in my project this really would make the whole module messy, as the function is not related to the module in general, but as having said, very specific to that method it is used in. In other words: splitting this to somewhere so far away from its context, it makes the code quite less readable, and less pythonic.
Is there any other way, or should I choose one of the three ways shown here?