11

I have a python module with a lot of variables. These variables are used and changed by many other modules. And I want to reload this module from itself when it happens (for refresh).

How to do that?

upd: so.. this is me, question author, 8 years after the question publication date. I have zero idea why I would try to do something like that then.

If you read this, please attempt to use proper configuration management techniques (most of the time supplied by your framework) instead.

# ==================================
# foo.py

spam = 100

def set_spam(value):
    spam = value
    foo = reload(foo) # reload module from itself

# ==================================
# bar.py

import foo
print foo.spam # I expect 100
# assume that value changes here from some another modules (for example, from eggs.py, apple.py and fruit.py)
foo.set_spam(200)
print foo.spam # I expect 200
4
  • 6
    This sounds like a fairly bad idea... Commented Dec 10, 2012 at 16:32
  • @Wooble, okay, so.. what would you recommend? wrap all those variables as Singleton Class? Commented Dec 10, 2012 at 16:38
  • 1
    In your particular case, I think you just want global spam at the top of your set_spam function, but it's not entirely clear to me why you think reloading could possibly keep the value at 200. If anything, it will reset it to 100 immediately after setting it, which makes no sense. Commented Dec 10, 2012 at 16:40
  • @Wooble better than relying on the user to reload(module) Commented Jan 21, 2021 at 16:42

7 Answers 7

3

In Python 2.6.2 it is simple. Assume your module is named "t" and is defined as follows:

import imp

def reload():
    name="t"
    imp.load_module(name,*imp.find_module(name))

print("loaded")

After you have loaded this module add another member it and execute t.reload().

p.s. I guess everybody thinks this is a bad idea: they're probably right, but if you're interactively developing a module maybe it makes things more convenient. Just take it out before you distribute your code to others or they might get confused.

Sign up to request clarification or add additional context in comments.

Comments

3

There are a number of problems with what you're trying to achieve, unless you're deliberately setting up a self-modifying code system, which it doesn't look like you are.

1. Global variables

spam = 100

def set_spam(value):
    spam = value
    foo = reload(foo) #reload module from itself

This is not going to work. Because of how Python closures work, your spam = value line is going to create a new local variable spam within your set_spam function, which then won't get used. To properly change the value of the global spam, you have to use the global keyword, as such:

spam = 100

def set_spam(value):
    global spam
    spam = value

2. Reloading modules "from themselves"

As far as I know, there's no way to actually do this, nor should you need to. Any module you've import-ed is called from some other module, all the way up to __main__. You would simply refresh it from that calling module. Yes, you could attempt to self-import a module (though there might be infinite loop issues, as mentioned by mgilson), but even then (using an example named "foo"), if you had it import itself, you'd just have foo.foo, and doing something like foo.reload(foo) (if that's even valid) would simply reload the sub-foo, not the base one.

3. Reloading foo.py at all

# ==================================
# foo.py

spam = 100

def set_spam(value):
    global spam
    spam = value

Note how at the top of this code, you're assigning 100 to spam. Every time you import the module, you'll be doing that again. So, even if you've already changed the value of spam in the code that's imported foo, when you reload the module, you'll actually be destroying the change you just made. Example:

>>> import foo
>>> foo.spam
100
>>> foo.spam = 9
>>> foo.spam
9
>>> reload(foo)
>>> foo.spam
100

So if you want to keep the changes you've made to the variable in foo, you should not reload the module. Furthermore, you really don't even need to use a set_spam function to change spam, you can just set it directly, as I did.

4. Trying to use this "changed" module value in other modules

Finally, if I understand correctly what you're trying to do, that's not going to work. This is in large part because of something I mentioned in part 3, wherein every time you load foo, the spam=100 line is going to reset the value of spam. In the same way, if you import the foo module in two different other modules, when each one imports it, they're each going to start out with spam = 100, completely independently of what the other module does with foo.spam. Example, if both bar1.py and bar2.py contain the line import foo:

>>> import bar1, bar2
>>> bar1.foo.spam
100
>>> bar2.foo.spam
100
>>> bar1.foo.spam = 200
>>> bar1.foo.spam
200
>>> bar2.foo.spam
100

With more explanation about what you're trying to do, we could help you restructure your code to make it work better.

Comments

2

Here

import sys

reload(sys.modules[__name__])

NB, this does not work with the main module but with any other module.

3 Comments

No go:-> ImportError: Cannot re-init internal module main
Good catch, it works with module other than main though.
This works, however if you're inside function older version of function gets executed - but then you can define wrapper - one main function with reload function call, and one wrapped function which gets updated and it will be called after reload()
2

You also can count the times the module was reloaded!:

mymodule.py

import importlib
import sys

count = 0
var = 0

def refresh():
    global count
    c = count
    importlib.reload(sys.modules[__name__])
    count = c + 1
    print (f'reloaded {count} time(s)')

main.py

import mymodule

mymodule.var = 'changed'
mymodule.refresh()
  # -> reloaded 1 times(s)
print(mymodule.var)
  # -> 0
mymodule.refresh()
  # -> reloaded 2 times(s)

1 Comment

the counter is reset to 0 every time the module is reloaded for the 4th line count = 0!
1

I don't see how you could do this cleanly without getting caught in an infinite loop. Maybe it would be better to have a reset function?

def reset():
    global spam
    spam = 100

Note that this design still seems a little funky to me. It seems like better data encapsulation would probably help you a lot in the long run -- Perhaps something like:

def new_namespace():
    return {'spam':100}

Then each of your other functions could accept a namespace dictionary as an argument rather than relying on the foo namespace.

Comments

0

Here is another version of the previous answer with the counter only initialized if it does not exist. Tested on python 3.8.6

# mod.py
import importlib, sys

a=0

def reload():
  
    importlib.reload(sys.modules[__name__])
    global i
    try:
        i += 1
    except:
        i = 1
    print(f'reloaded {i} time(s)')

# main.py
import mod

mod.a = 10
print (mod.a) # -> 10
mod.reload() # -> reloaded 1 time
print (mod.a) # -> 0
mod.reload() # -> reloaded 2 times

Comments

-1

This operation doesn't make sens at all. From Python documentation:

reload(module): Reload a previously imported module. The argument must be a module object, so it must have been successfully imported before.

So, reload make sens only from context of other module which performed import previously.

2 Comments

Well, it's in sys.modules globally, so it's not as nonsensical as it might sound. still...
Obviously the module should not be reloaded from the global scope but from a function which should be called from another module!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.