0

Suppose I have a class with several public methods and several _private or "helper" method.

There is the problem of how to sort them in the code. It could be 1. all public then all private or 2. the private functions after the calling public functions. (See Best practice: ordering of public/protected/private within the class definition?

An alternative approach is nesting private functions but that has a runtime overhead.

How could be the code structured in order to easily peek:

  • The interface of the class
  • The logic structure of the functions?
jps
  • 20,041
  • 15
  • 75
  • 79
nadapez
  • 2,603
  • 2
  • 20
  • 26
  • There are lots of ways you can order them, but virtually all of them produce the same result. Voting to close as opinion-based. – chepner Jan 22 '23 at 16:25
  • You should be *documenting* your public interface, so that people don't need to read the class source code to discover it. (As a side effect, the undocumented private methods won't be generally known, and anyone calling them in a way you didn't intend them to be called can suffer the consequences of using undocumented code.) – chepner Jan 22 '23 at 16:27
  • there are no private methods in Python, everything is public. So, it isn't clear what you're trying to achieve. Also, we don't need to create interfaces (because Python has multiple inheritance and ducktyping). python is not Java – Alex Feb 07 '23 at 22:16

1 Answers1

0

This is my own answer but I would be niced to see other alternatives.

I will show it using functions instead of methods, but the same applies to methods of course.

My approach is to create a decorator:

#  helper.py

def helper(func):
    def decorator(helper):
        helper.__name__ = '{func.__name__}.{helper,__name__}'

ant then use it like this:

from helper import helper 

# interface {

def public_func():
    public_func.helper1()
    public_func.helper2()

def if_the_function_has_a_long_name_you_can():
    this = if_the_function_has_a_long_name_you_can
    ...
    this.helper3()


# interface }


# private {

@helper(public_func)
def _helper1():
    print('helper1')


@helper(public_func)
def _helper2():
    print('helper2')
    _helper2.nested_helper()


@helper(if_the_function_has_a_long_name_you_can)
def _helper3():
    print('helper3')


@helper(_helper2)
def _nested_helper():
    print('nested')


# private }



def not_polite():
    public_func.helper1()
    public_func.helper2.nested_helper() 

Pros:

  • The code structure is flat, there is no classes nor functions inside functions
  • Still there is structure but serving only as documentation
  • You can create arbitrary nested virtual structures without even creating a class or nesting functions. The structure is only expressed by the doted names: functionx.helper1.helper12.helper121
  • Debugging is easier because you see the calling order only by function name!
  • Underscore _ is only used at definition of the helper functions
  • Is easy to see what are the helper functions and which function they serve.
  • The helper functions can be called from anywhere even from outside the module, (although not polite)
  • The helper functions can still be called by its original name _helperx
  • But the name of the helper function which would appear in a traceback has the dotted style functionx.helpery

Cons

  • Adding the helper functions as attributes of the public functions confuse the code analysis of the IDEs, so you don't have code completion for the dotted style.
  • You have the coding overhead of:
    • importing the decorator
    • decorate the helpers
  • any other ?
nadapez
  • 2,603
  • 2
  • 20
  • 26