Search code examples
pythonencapsulationmodularity

Python Function Encapsulation (to avoid accidental invocations without proper context)


I'm cobbling together a small, internal app, dumping it from my head in a "bottom-up" way.

As such, once I have discovered that a function I'm writing works, I'd like to make it a "helper" function and prevent myself from coming back in an hour or a year and writing code that invokes it in a way I didn't meant to invoke it.

In Java, I could just make a class, make my "helper" functions private static methods, and put the code I "mean to" invoke them from into a public static method.

What's the pythonic way to avoid stepping on your own toes as I've described?

Here's a really simple example function I don't want to let myself call from "just anywhere":

(For example, maybe for editability I'd like to keep all functions that need the "simple_salesforce" module close together but don't want to expose functions like getAFreshContactList() to invocation outside of nicely exception-handled code controlling the flow of login, determining whether a fresh Contact list is even needed, logout, etc.)

from simple_salesforce import Salesforce
def getAFreshContactList(sfSession):
    if isinstance(sfSession, Salesforce):
        return sfSession.query("SELECT Id, Name, Email FROM Contact LIMIT 2")['records']

Thank you!


Solution

  • Add the function to a separate helper module. Do not import the module using * notation and the function calling convention will behave exactly like that of a Java static function. For example:

    my_helper.py:

    from simple_salesforce import Salesforce
    def getAFreshContactList(sfSession):
        if isinstance(sfSession, Salesforce):
            return sfSession.query("SELECT Id, Name, Email FROM Contact LIMIT 2")['records']
    

    my_program.py

    import my_helper
    def someFunction:
        my_helper.getAFreshContactList(None)
    

    Virtually the entire point of Python namespaces is exactly to create a separation between names that serve different purposes.

    While python convention allows you to prepend an underscore to a module attribute to prevent it from appearing in docs and star imports, there is no such thing as truly private in Python:

    “Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.

    In Java you would have to use reflection to call the privately declared methods of a class, but in Python there is never anything to stop you from invoking my_helper._myPrivateFunction() directly besides convention. Prepending an underscore to a function or any other attribute should just serve to remind you to be extra careful when you use it outside the module.