0

How can I call a common submodule (present in various modules), selecting the right module from a value?

Example:

Let's say I have this folder structure:

myprogram/
          myprogram.py          #main program
          colors/               #colors package
                 __init__.py    #contains __all__
                 blue/          #blue module
                      paint.py  #submodule
                 red/           #red module
                      paint.py  #submodule

The paint submodule has the same name in every module, but different code in each.

In myprogram.py I want to do something like this:

import colors   #import all the colors modules

my_shape = circle()   
my_color = "blue"     #define the module to call by his name
if my_color in colors:
  colors.my_color.paint(my_shape)    #calls the submodule from the right mosule

I want the "colors" package to be scalable, so I can easy remove one color from one deploy with the minimum effort..

(I can make a single paint.py module with a case of inside, but it's not easily scalable)

The questions are:

  • Is it possible to do the if my_module in package thing?
  • If so, haw can I call a package's module using a variable package.my_module_variable.function()?

I'm trying to solve this storing functions into dicts, am I in the right way?

Community
  • 1
  • 1
Hrabal
  • 2,403
  • 2
  • 20
  • 30
  • You don't provide details about how `paint.py` works, but I would say that it makes more sense to have one `paint.py` which contains different colors or can be instantiated with the color of choice. Instead of a big "if" inside `paint`, you could instead use a `dict` which should scale well. This is assuming that colors behave the same. IIRC, you can search for a module in a package, store it and then call it, but that's an overkill/unelegant way of doing it imho. – NordCoder Jun 25 '14 at 16:27
  • you can have a look at this related SO question about [dynamic module import](http://stackoverflow.com/questions/301134/dynamic-module-import-in-python) – toine Jun 25 '14 at 16:53

2 Answers2

1

You should be able to treat the color submodules as attributes. The submodules will need __init__.py files in their directorys (ex. blue/__init__.py) along with the paint.py module. Then you could do something like this:

my_color = "blue"
if hasattr(colors, my_color):
    getattr(colors, my_color).paint(my_shape)

Note however that in your example paint is a module so you wouldn't be able to call the module. If there is a paint function in the paint module then you would do:

getattr(colors, my_color).paint.paint(my_shape)

Edit: I should mention, like others have, that this seems overkill for the example you've shown. If your real code/situation is more complex than this then go for it. Otherwise a nice alternative might be to have one paint module with dictionaries for various operations. So a file like colors/paint.py could contain:

def paint_blue(shape):
    print("BLUE!")

def paint_red(shape):
    print("RED!")

paint_funcs = {
    "blue": paint_blue,
    "red": paint_red,
}

def paint(color, shape):
    return paint_funcs[color](shape)

Edit 2:

The __init__.py files inside the color directories are needed to treat them as subpackages, otherwise you can't import them. My initial method of using attributes like this won't work unless the main package knows about the submodules. So for example:

import colors
# colors doesn't know it has submodules 'blue' and 'red'
from colors import *
# if the colors __init__ has an __all__ declaration now it does know about them
hasattr(colors, "blue") # will work

Or you can import the colors in the colors __init__.py module

# in colors/__init__.py
import blue
import red

Or you can do what others have said about dynamic imports.

djhoese
  • 3,567
  • 1
  • 27
  • 45
  • How can we treat module as attribute? I am not sure adding `__init__` will treat blue as attribute... :) – Nikhil Rupanawar Jun 25 '14 at 17:18
  • A module is just an object that has attributes like any other attribute. You can test this with any module like `time`, try `import time; hasattr(time, "sleep")`. The `__init__.py` in the subdirectories makes the color folders into subpackages. Otherwise they can't be imported. – djhoese Jun 25 '14 at 17:28
  • Scratch that, the time module example works because it has the function `sleep`. I'll edit my answer in a second to show how I got it to work. – djhoese Jun 25 '14 at 17:31
  • It worked, thank you! P.S: in my case the paint.py submodules will be complex, but your first edit works too ;). – Hrabal Jun 26 '14 at 10:00
0

you can load module dynamically with the imp module, provided you know the path to the module file:

import imp

my_color = "blue"
paint = imp.load_source('paint', 'colors/'+my_color+'/paint.py')

and paint can be used as a regular module, like if it was imported with:

from colors import blue.paint as paint
toine
  • 1,946
  • 18
  • 24