如何使在Python函数中通过exec("import xxx")创建的模块可用?
如何使在Python函数中通过exec("import xxx")创建的模块可用?
我编写了一个简单的函数,它会筛选我的Python文件夹并查找可能的模块位置。我想要做的很简单,只需传递一个模块导入的字符串,函数将找到模块的文件夹,切换到该文件夹,并将其导入到我正在工作的环境中,例如:\nanyimport(\'from fun_abc import *\')
\n最初我尝试了以下代码:\n
class anyimport(object): def __init__(self, importmodule, pythonpath='/home/user/Python', finddir=finddir): ################################################################## ### A BUNCH OF CODES SCANNING THE DIRECTORY AND LOCATE THE ONE ### ################################################################## ### "pointdir" is where the directory of the file is ### ### "evalstr" is a string that looks like this : --- ### 'from yourmodule import *' os.chdir(pointdir) exec evalstr
\n由于我在iPython Notebook中编写了整个代码,它是有效的。因此,问题就这样溜走了。然后我发现它无法正常工作,因为函数导入的模块停留在函数的局部变量空间中。\n然后我在Stack Overflow上找到了这个讨论\"In Python, why doesn\'t an import in an exec in a function work?\"。因此,我将代码更改为以下内容:\n
class anyimport(object): def __init__(self, importmodule, pythonpath='/home/user/Python', finddir=finddir): ################################################################## ### A BUNCH OF CODES SCANNING THE DIRECTORY AND LOCATE THE ONE ### ################################################################## ### "pointdir" is where the directory of the file is ### ### "evalstr" is a string that looks like this : --- ### 'from yourmodule import *' sys.path.append(os.path.join(os.path.dirname(__file__), pointdir)) exec (evalstr, globals())
\n它仍然无法正常工作。函数运行时没有错误,但是模块对我不可用,例如如果我在其中运行script.py
,其中我执行anyimport(\'from fun_abc import *\')
,但是fun_abc
中没有任何内容。Python会告诉我“NameError: name \'fun_you_want\' is not defined”。\n是否有人能够指点我正确的方向来解决这个问题?\n感谢您的关注,我非常感谢您的帮助!\n
注意:
\n除了@Noya的回答指出必须传递作用域以使exec
起作用之外,为了避免“ImportError”,您需要在运行exec
之前添加以下行:\n
sys.path.append(os.path.join(os.path.dirname(__file__), pointdir)) exec (evalstr, scope)
\n这是因为我们修改了sys.path
,假设当前工作目录始终在main/
中。我们需要将父目录添加到sys.path
中。请参阅这个Stack Overflow讨论\"ImportError: No module named - Python\"以获取有关解决此问题的更多信息。
问题的原因是在Python函数中使用exec("import xxx")语句导入模块后,模块在函数的局部作用域中不可用。解决方法是使用compile函数和exec语句将导入模块的代码编译为可执行的对象,然后通过更新globals()和sys.modules来使模块在全局作用域中可用。
具体的解决方法如下:
1. 创建一个空字典_globals,用于存储exec语句执行后的全局变量。
2. 使用compile函数将导入模块的代码字符串编译为可执行的对象。代码字符串可以包含多个import语句。
3. 使用exec语句将编译后的代码对象在_globals中执行,将模块导入到_globals中。
4. 导入sys模块,并使用globals()函数获取全局作用域的字典对象g。
5. 使用g.update(_globals)将_globals中的变量更新到全局作用域中。
6. 使用sys.modules.update(_globals)将_globals中的模块更新到sys.modules中。
下面是一个具体的示例代码:
def foo(import_string): _globals = {} code = compile(import_string, '', 'exec') exec code in _globals import sys g = globals() g.update(_globals) sys.modules.update(_globals) foo('import numpy as np') print(np.linspace)
在上面的代码中,我们定义了一个名为foo的函数,接受一个导入模块的字符串作为参数。在函数内部,我们创建了一个空字典_globals用于存储exec语句执行后的全局变量。然后使用compile函数将导入模块的字符串编译为可执行的对象code,并使用exec语句在_globals中执行该code,将模块导入到_globals中。接着导入sys模块,并使用globals()函数获取全局作用域的字典对象g。最后使用g.update(_globals)将_globals中的变量更新到全局作用域中,使用sys.modules.update(_globals)将_globals中的模块更新到sys.modules中。最后我们调用foo函数,传入字符串'import numpy as np',并打印出np.linspace的结果。
在上面的示例中,我们可以看到,通过使用compile和exec函数,我们成功地将导入模块的代码在函数中执行,并使得模块在全局作用域中可用。
文章标题:如何在Python函数中将exec("import xxx")的模块导入语句的模块可用
在Python中,我们可以使用exec
函数在当前作用域中执行代码。在函数内部,这意味着它会在函数的本地作用域中执行。如果我们希望exec
函数将变量放在其他作用域中,我们可以通过给exec
函数传递一个元组(code, scope)
来实现。例如,我们可以使用globals()
函数将变量放在模块级别的作用域中。需要注意的是,globals
函数始终返回当前模块的字典(在函数或方法内部,这是定义它的模块,而不是调用它的模块)。
在上面的例子中,我们需要将期望的作用域传递给我们的实用函数。具体实现如下:
anyimport.py:
class anyimport(object): def __init__(self, importmodule, scope): exec (importmodule, scope)
test.py:
a = 42 b = 'foo'
main.py:
from anyimport import anyimport if __name__ == '__main__': anyimport('from test import *', globals()) # 42 foo print a, b
运行python main.py
进行测试,确保所有文件都在当前目录中。
另一种解决方法
如果你不想使用exec
函数,还有一种更优雅的方法可以使用Python提供的导入工具。下面的代码与from some.package import *
等效:
[...] 更方便的方法是使用
importlib
:
globals().update(importlib.import_module('some.package').__dict__)
。
感谢你的回答!我已经测试过你的代码,它们可以正常工作!受到鼓舞,我相应地修改了我的代码,但是现在出现了一个新问题:“ImportError: No module named fun_abc”,我使用os.listdir(pointdir)
检查了一下,.py文件确实存在!出了什么问题??
我添加了sys.path.append(os.path.join(os.path.dirname(__file__), pointdir))
,结果回到了起点,错误又变成了“NameError: name 'getnearpos' is not defined”。=.=
要进一步帮助你,我们需要一个能够重现你描述的错误的最小代码示例(就像这个答案中的示例代码一样)。
我想我已经使用你的代码重现了这个错误。你可以尝试将test.py
放在另一个文件夹中。然后创建一个名为tryimport.py
的文件,将代码放在if __name__ == '__main__'
下面。在tryimport.py
中,你需要首先从anyimport.py
中导入anyimport
。然后尝试运行python tryimport.py
。我发现了以下错误:Traceback (most recent call last): File "stack.py", line 9, in <module>; print a, b; NameError: name 'a' is not defined
太棒了!它起作用了!感谢你详细的回答和非常友好的跟进!