0

I have a bunch of methods which all look something like this:

def myFunc():
    ...
    {initialise stuff}
    {do stuff}
    {finalise stuff}
    ...

where {initialise stuff} and {finalise stuff} are identical for each method, and {do stuff} may use variables defined in {initialise stuff}.

To avoid repetition, I'd like to put {initialise stuff} and {finalise stuff} in a separate method, which could be called from inside myFunc(). So, something like this:

def wrap(innerMethod):
    vars = {initialise stuff}
    innerMethod(vars)
    {finalise stuff}

def myFunc():
    ...
    wrap(lambda vars :
        {do stuff}
    )
    ...

Unfortunately, it seems that code blocks can't be passed as arguments in Python (unless this feature has been added in the last 2 years). Therefore, this doesn't seem to work if {do stuff} is longer than a single line. I could put {do stuff} into a separate method, but I'd rather not since:

  • The {do stuff} code will never be reused (it is different for each method);
  • It makes the program flow more confusing / less logical.

Is there another way to put {initialise stuff} and {finalise stuff} into a separate method?

2 Answers2

0

You can try functools.wraps and decorator:

import functools

def mywrapper(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        print("{initialize_stuff}")
        result = f(*args, **kwargs)
        print("{finalize_stuff}")
        return result
    return wrapper

@mywrapper
def myFunc():
    print("{innerMethod}")

Output:

>>> myFunc()
{initialize_stuff}
{innerMethod}
{finalize_stuff}
Corralien
  • 109,409
  • 8
  • 28
  • 52
0

Context managers provide the best solution - thanks to @user2357112 for the suggestion.

from contextlib import contextmanager

@contextmanager
def wrap():
  {initialise stuff}
  yield {stuff-to-return-to-method}
  {finalise stuff}

def myFunc():
  with wrap() as vars:
    {do stuff}

You can even pass arguments to wrap and use them in the initialisation.

If you want {finalise stuff} to run even if the wrapped code throws an exception (which you usually do want with context managers), you should wrap the yield in a try and put the finalization in a finally:

@contextmanager
def wrap():
  {initialise stuff}
  try:
    yield {stuff-to-return-to-method}
  finally:
    {finalise stuff}
user2357112
  • 260,549
  • 28
  • 431
  • 505