Python __import__ 参数混淆

7 投票
6 回答
5362 浏览
提问于 2025-04-15 23:21

我正在尝试导入一个模块,同时传递一些全局变量,但似乎没有成功:

文件 test_1:

test_2 = __import__("test_2", {"testvar": 1})

文件 test_2:

print testvar

这看起来应该可以工作,并且打印出1,但当我运行 test_1 时却出现了以下错误:

Traceback (most recent call last):
  File ".../test_1.py", line 1, in <module>
    print testvar
NameError: name 'testvar' is not defined

我哪里做错了?

编辑:

正如我后面评论的那样,这是一个尝试替换图形库中函数的过程。这里有一个示例程序(是我老师写的),使用了那个库:

from graphics import *
makeGraphicsWindow(800, 600)

############################################################
# this function is called once to initialize your new world

def startWorld(world):
    world.ballX = 50
    world.ballY = 300
    return world

############################################################
# this function is called every frame to update your world

def updateWorld(world):
     world.ballX = world.ballX + 3
     return world

############################################################
# this function is called every frame to draw your world

def drawWorld(world):
    fillCircle(world.ballX, world.ballY, 50, "red")

############################################################

runGraphics(startWorld, updateWorld, drawWorld)

请注意,这段代码是为了让那些从未(或几乎未)见过任何代码的人(不仅仅是 Python)能够轻松理解。

重写函数的示例:

原始代码:

def drawPoint(x, y, color=GLI.foreground):
    GLI.screen.set_at((int(x),int(y)), lookupColor(color))

注入的代码:

# Where self is a window (class I created) instance.
def drawPoint(x, y, color = self.foreground):
  self.surface.set_at((int(x), int(y)), lookupColor(color))

我想我真正想问的是:我该如何在模块运行之前,将全局函数/变量注入到导入的模块中呢……?

6 个回答

2

如上所述,__import__ 这个函数不会把全局变量添加到新的模块里。如果你想做到这一点,我建议你添加一个初始化函数,比如叫 initialize(),这个函数可以把值赋给一个事先声明好的全局表,像这样:

gTable = {}
gInited = False

def initialize(table):
    if(not gInited):
        gTable = table
        gInited = True

def getGlobal(name):
    if(name in gTable):
        return gTable[name]
    else:
        throw NameError("name '"+name+"' is not defined")

def setGlobal(name, value):
    gTable[name] = value

def mod_main():
    print getGlobal("testvar")

然后你可以这样导入它:

import modGlobalsTest
modGlobalsTest.initialize({"testvar": 1})
modGlobalsTest.mod_main()

如果你想修改导入库里的函数,可以这样做:

import foobar
def bar(x):
    return x+1
foobar.foo = bar

这样就可以用自定义的函数 bar 替换掉 foobar.foo

7

根据文档的解释,__import__globals参数并不会像你想的那样“注入”额外的全局变量到导入的模块中。实际上,通常传入的就是导入模块的globals(),所以如果真的发生这种注入,那会引发很多问题。文档中明确提到:

标准实现根本不使用它的局部参数,只用全局参数来确定导入语句的包上下文。

补充说明:如果你确实需要注入,比如在一个空的导入模块中,正如提问者在评论中提到的,只要你在导入之后立刻进行,这很简单:

themod = __import__(themodname)
themod.__dict__.update(thedict)

导入模块的主体不会意识到后续的注入,但如果这个主体本身是空的,那就没关系了;-)。在导入之后,你可以随心所欲地进行注入,所有后续对该模块的使用都会把你的注入视为真正的模块级别的绑定名称(因为它们确实是;-)。

如果你愿意,甚至可以省去一开始就需要一个空的.py模块...:

import new, sys
themod = new.module(themodname)
sys.modules[themodname] = themod
themod.__dict__.update(thedict)

补充说明:提问者在问题的编辑中试图澄清...:

我想我真正的问题是:我如何在模块运行之前将全局函数/变量注入到导入的模块中...?

“模块运行”这个说法其实不太准确——模块是被加载的(这会执行它的主体),而且只会加载一次(除非之后重新加载)。它们并不会“运行”本身。对于一个空模块,你首先导入它(这不会执行任何操作,因为模块是空的),然后如果你愿意,可以用themodule.__dict__.update和属性赋值的组合来填充这个模块——函数不会在提到时自动调用(正如提问者在评论中表达的担忧),所以在这方面它们可以像其他变量一样对待(而且在大多数其他方面也是如此,这就是为什么Python被称为具有一流函数的语言)。

3

我之前也遇到过同样的问题……

然后我用了一种方法来解决它……

我不知道这对你有没有帮助!

文件2: test1.py

global myvar
myvar = "some initial value"
print myvar
test = __import__("test2")
print myvar

文件2: test2.py

import inspect
stk = inspect.stack()[1]
myvar = inspect.getmodule(frm[0]).myvar
print myvar
myvar = "i am changing its value."

这里的变量是从模块1传递到模块2的,而且是通过引用传递的。

撰写回答