0

I am writing a Python3 program, in which I need to be able to override some method on the fly. My folder structure is more like:

./
prog.py
methods/
  add.py
  minus.py

In prog.py I want to call a calc() function which is defined both in add.py and minus.py. I wish that the code can work as if:

def prog('foo'):
    from method.foo import calc
    calc()

But importing in the middle of a function seems awful and might slow down the whole program. Is there any workaround that can achieve the same effect?

I am trying to be flexible so that I can add more methods later on, so I avoid if statements and import all the modules at once.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
7O'clock
  • 41
  • 9
  • Is there something preventing you from just calling the functions in the branches of the if statement, or is it something more complicated than the example shows? – MutantOctopus Mar 11 '18 at 11:01
  • No, but I am thinking of add different calc() from time to time. In reality I hope to realize that prog(add) imports method.add without using if statements. I will modify my question... @BHustus – 7O'clock Mar 11 '18 at 11:05
  • Ohh, so you want to be able to dynamically load a module from some path? – MutantOctopus Mar 11 '18 at 11:06
  • 1
    If you want to *dynamically import* something, there are some methods to do that at runtime. I'm not going to post this as a formal answer because I haven't tested this and I wouldn't recommend taking my word at face value, but look into [`importlib.import_module`](https://docs.python.org/3/library/importlib.html#importlib.import_module) and judicious use of `del`, see if that gets what you need. – MutantOctopus Mar 11 '18 at 11:11
  • That is correct... with out causing troubles.:) @BHustus – 7O'clock Mar 11 '18 at 11:11
  • And for reference, [here is a question re: dynamic imports](https://stackoverflow.com/questions/301134/dynamic-module-import-in-python). Your biggest concern, I think, would be name collisions if you include multiple modules with the same-named function. Hence, judicious use of `del` as a final statement in the function, *maybe*. – MutantOctopus Mar 11 '18 at 11:13
  • @BHustus: or, just use the module name as the target. And a dictionary, since the names are built dynamically. – Martijn Pieters Mar 11 '18 at 11:19

1 Answers1

5

You have 2 options:

  • Import the modules, and use the module name with calc attributes.
  • Import the calc functions under alternate names with from ... import ... as

Either way, I'd store the function references in a dictionary, rather than use if .. elif to pick one.

The first approach

from method import add
from method import minus

calc_functions = {
    'add': add.calc,
    'minus': minus.calc,
}

def prog(method):
    return calc_functions[method]()   

or the second:

from method.add import calc as addition
from method.minus import calc as subtraction

calc_functions = {
    'add': addition,
    'minus': subtraction,
}

def prog(method):
    return calc_functions[method]()   

If you needed to import modules dynamically, then use importlib.import_module(), no need to worry about name clashes:

import importlib

def prog(method):
    try:
        calc_module = importlib.import_module('method.' + method)
    except ModuleNotFoundError:   # or ImportError in Python < 3.6
        raise ValueError('No such method {!r}'.format(method))
    return calc_module.calc()

    return calc_functions[method]()   
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • This is neat, but in my case I will need to change the code every time I add some new calc() methods. Is it possible to do it dynamically? – 7O'clock Mar 11 '18 at 11:13
  • 1
    @7O'clock: yes, you can import modules dynamically, see [Dynamic module import in Python](//stackoverflow.com/q/301134) – Martijn Pieters Mar 11 '18 at 11:18