I'd like to define a helper function that has the ability to modify a module-level variable (with known name) from surrounding context without explicitly passing it, e.g.
# mod1.py
mod_var = 1
modify_var()
# mod_var modified
print mod_var
The problem is - I can't reference variable by mod1.mod_var
, because I want to use helper function across many modules (helper itself will be defined in other module); it should dynamically 'pick' mod_var from surrounding calling context/scope.
Is this possible? How to obtain this?
My use case is to enhance defining URL -> view mapping in D开发者_StackOverflow社区jango. Those definitions are spread across many sub-modules that define urlpatterns
module-level variable. Helper function should pick this variable from the module that calls it and modify it. Avoiding explicitly passing it as argument would be great.
Edit: For additional solution - check this answer.
Edit2:
Wrong solution below! (left for references in comments)
Recently I've found another solution (the least magical in my opinion ;))
modify_var()
function could be implemented like this:
def modify_var():
calling_module = __import__("__main__")
calling_module.mod_var = 42
Still, potential profits are arguable.
unittest
module uses this technique in its main
method.
It's a truly bad, horrible, and awful idea, which will lead to future maintenance nightmares. However, Python does offer "enough rope to shoot yourself in the foot", if you truly insist: introspection and metaprogramming tools which are mostly intended for debugging purposes, but can be abused to perform the ill-conceived task you so desperately crave.
For example, in evil.py
:
import inspect
def modify_var():
callersframe = inspect.stack()[1][0]
callersglobals = callersframe.f_globals
if 'mod_var' not in callersglobals:
raise ValueError, 'calling module has no "mod_var"!'
callersglobals['mod_var'] += 1
now say you have two modules, a.py
:
import evil
mod_var = 23
evil.modify_var()
print 'a mod_var now:', mod_var
and b.py
:
import evil
mod_var = 100
evil.modify_var()
print 'b mod_var now:', mod_var
you could do:
$ python -c'import a; import b'
a mod_var now: 24
b mod_var now: 101
However, maintaining this kind of black-magic tricks in the future is going to be a headache, so I'd strongly recommend not doing things this way.
What you want to do sounds like too much magic. Pass in urlpatterns and be done with it. Explicit is better than implicit.
OK, here's the magic, but again, I recommend not using it:
import sys
def modify_var():
"""Mysteriously change `mod_var` in the caller's context."""
f = sys._getframe(1)
f.f_locals['mod_var'] += " (modified)"
mod_var = "Hello"
modify_var()
print mod_var
prints:
Hello (modified)
As a further warning against this technique: _getframe
is one of those functions that other implementations of Python don't provide, and the docs include this sentence: "This function should be used for internal and specialized purposes only."
If you really want to do that then you'll need to import mod1 in either the other module or directly in the function, and then modify it off that import. But don't do that; seasoned programmers will point and laugh.
精彩评论