3

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?

Iodnas
  • 3,154
  • 20
  • 25

2 Answers2

0

I sure hope the real example is more complicated :)

I'd say go for nested or helper function. I'd personally go for the helper function but that's just my opinion. This is of course from what I can tell of your description. The actual code would provider better ground for a suggestion than just a random example.

pypat
  • 1,040
  • 8
  • 19
0

Personally, I think either option 1 or 2 is the cleanest. Option 3 is very context specific to be defined as a module function, as you mention.

To decide between option 1 or 2, you need to decide how important it it to be able to unit test that particular piece of code. Considering that you've already basically answered the question of the importance of unit testing and if it can be unit tested from within foo(),

And I really don't want to test this as part of foo()

then you'll need to go with some variant of option 2. Ideally private methods would be the way to go here, but since python doesnt support that, at least consider naming it __plus_two() Here is a related question about the leading double underscore in Python: What is the meaning of a single- and a double-underscore before an object name? Here is another question related to private methods: Why are Python's 'private' methods not actually private?

Community
  • 1
  • 1
Brady
  • 9,873
  • 1
  • 18
  • 56
  • Thinking further your suggestion, in place of an instance method, do you think a ``staticmethod``, not being related to instance nor class, but to "something that is class related stuff", is more appropriate then? (Well, it would still be placed in a much wider context than it needs to be, but at least not so far as if being a module function.) – Iodnas Jun 05 '13 at 13:04
  • Would a static underscore method be the best choice? – Iodnas Jun 05 '13 at 13:10
  • We dont have all the context of the real situation to know if making the function static makes sense or not. Typically static functions perform some sort of functionality related the class without needing an instance of the class, and that doesnt seem to be the case here. I think the best bet here would be to use the double underscore, and comment the function well, stating its purpose. – Brady Jun 05 '13 at 13:32