如何在函数中获取另一个模块的变量?

3 投票
4 回答
2052 浏览
提问于 2025-04-15 17:52

我想定义一个辅助函数,这个函数可以在不直接传递的情况下,修改一个模块级别的变量(名字是已知的),比如说:

# mod1.py
mod_var = 1
modify_var()
# mod_var modified
print mod_var

问题是,我不能通过 mod1.mod_var 来引用这个变量,因为我想在多个模块中使用这个辅助函数(这个辅助函数本身会在其他模块中定义);它应该能够动态地从调用它的上下文中“挑选”出 mod_var。

这可能吗?我该怎么做呢?

我的使用场景是为了在 Django 中增强 URL 到视图的映射定义。这些定义分散在多个子模块中,这些子模块定义了 urlpatterns 这个模块级别的变量。辅助函数应该从调用它的模块中获取这个变量并进行修改。如果能避免将它作为参数显式传递,那就太好了。

编辑: 如果想要更多的解决方案,可以查看 这个答案。


编辑2:

下面的解决方案是错误的!(保留在评论中以供参考)

最近我发现了另一种解决方案(在我看来是最不神奇的;)

modify_var() 函数可以这样实现:

def modify_var():
    calling_module = __import__("__main__")
    calling_module.mod_var = 42

不过,潜在的好处还有待讨论。

unittest 模块在它的 main 方法中使用了这种技术。

4 个回答

2

好的,这里有个神奇的东西,但我还是建议你不要用它:

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

打印结果是:

Hello (modified)

再给你一个警告,关于这个技巧:_getframe 是一个其他Python版本可能没有的函数,而且文档里有这么一句话:“这个函数应该只用于内部和特殊目的。”

4

你想做的事情听起来有点复杂。直接传入网址模式(urlpatterns),就可以搞定了。明确的东西总比模糊的好。

4

这真的是个糟糕透顶的主意,会让你在未来的维护中遇到很多麻烦。不过,Python确实给了你“足够的绳子让自己绊倒”的机会,如果你真的坚持的话:它有一些工具可以让你检查和修改代码,虽然这些工具主要是用来调试的,但也可以被滥用来完成你想做的那些不太合理的事情。

比如,在 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

假设你有两个模块,a.py

import evil

mod_var = 23
evil.modify_var()
print 'a mod_var now:', mod_var

还有 b.py

import evil

mod_var = 100
evil.modify_var()
print 'b mod_var now:', mod_var

你可以这样做:

$ python -c'import a; import b'
a mod_var now: 24
b mod_var now: 101

不过,未来维护这种黑魔法的技巧让你头疼,所以我强烈建议不要用这种方式来做事情。

撰写回答