[Tutor] aliasing an imported module

Steven D'Aprano steve at pearwood.info
Sun Feb 14 01:41:41 CET 2010


On Sat, 13 Feb 2010 10:51:38 am Garry Willgoose wrote:

> I want to be able to import multiple instances of a module and call
> each by a unique name and it doesn't appear at first glance that
> either import or __import__ have what I need. 

No, such a thing is not officially supported by Python.

Python treats modules as singletons -- there is only one instance of any 
module at any time. It is possible to force Python to break that 
promise, but that is fighting the language, and there's no guarantee 
that it won't cause other breakages further along. So if you choose to 
create multiple instances of a module, you're doing something the 
language doesn't want you to do, and if you end up shooting yourself in 
the foot you'll have no one to blame but yourself.

Having said that, the following trick should do what you want. Start 
with a simple module holding state:

# mymodule.py
state = []

Here's Python's normal behaviour:

>>> import mymodule
>>> mymodule.state
[]
>>> mymodule.state.append(123)
>>> mymodule.state
[123]
>>> import mymodule as something_else
>>> something_else.state
[123]


And here's the trick:

>>> import sys
>>> del sys.modules['mymodule']
>>> import mymodule as another_name
>>> another_name.state
[]
>>> mymodule.state  # Check the original.
[123]


This *should* work across all versions and implementations of Python, 
but remember that it's a hack. You're fighting the language rather than 
working with it.

Another problem is that there's no guarantee that the module holds all 
its state inside itself: it might in turn import a second module, and 
store state in there. Many packages, in particular, may do this.

The best solution is to avoid global state if you possibly can.


You also say:

> The key problem is that  
> the  module might locally store some partial results ready for the  
> next time its called to save CPU time (typically the results for one  
> timestep ready for the next timestep).


I'm going to take a wild guess as to what you're doing, and make a 
suggestion for how you can do something better.

I guess you have functions something like this:

STATE = None

def func():
    global STATE
    if STATE is None:
        # No global state recorded, so we start from scratch.
        step = 1
        partial = 0
    else:
        step, partial = STATE
    step += 1
    partial = do_some_calculations(step, partial)
    STATE = (step, partial)
    return partial

But as you point out, that means all calls to func() use the same global 
state.

Here are two alternatives. Here's a rather messy one, but it tickles my 
fancy: use a token to identify the caller, so each caller gets their 
own state and not somebody else's.

LAST_TOKEN = 0
def get_token():
    global LAST_TOKEN
    LAST_TOKEN += 1
    return LAST_TOKEN

STATE = {}

def func(token):
    global STATE
    if not STATE[token]:
        # No global state recorded, so we start from scratch.
        step = 1
        partial = 0
    else:
        step, partial = STATE[token]
    step += 1
    partial = do_some_calculations(step, partial)  # Defined elsewhere.
    STATE[token] = (step, partial)
    return partial


Then, before each independent use of func, the caller simply calls 
get_token() and passes that to the function. I'm sure you can see 
problems with this:

- the caller has to store their tokens and make sure they pass the right 
one;
- the function needs to deal with invalid tokens;
- the global STATE ends up storing the result of intermediate 
calculations long after they are no longer needed;
- the separation is a "gentleman's agreement" -- there is nothing 
stopping one caller from guessing another valid token.

Although I'm remarkably fond of this solution in theory, in practice I 
would never use it. A better solution is to write func in a more 
object-oriented fashion:

class FuncCalculator(object):
    """Class that calculates func"""
    def __init__(self):
        # Start with initial state.
        self.STATE = (1, 0.0)
    def __call__(self):
        step, partial = self.STATE
        step += 1
        partial = self.do_some_calculations(step, partial)
        self.STATE = (step, partial)
        return partial
    def do_some_calculations(self, step, partial):
        return partial + 1.0/step  # or whatever

Instances of the class are callable as if they were functions. In C++ 
terminology, this is called a "functor". In Python, we normally just 
call it a callable.

So we can create as many independent "functions" as needed, each with 
their own state:

>>> f = FuncCalculator()
>>> g = FuncCalculator()
>>> h = FuncCalculator()
>>> f()
0.5
>>> f()
0.83333333333333326
>>> f()
1.0833333333333333
>>> g()
0.5
>>> g()
0.83333333333333326
>>> h()
0.5
>>> f()
1.2833333333333332


Hope this helps!


-- 
Steven D'Aprano


More information about the Tutor mailing list