如何使在Python函数中通过exec("import xxx")创建的模块可用?

4 浏览
0 Comments

如何使在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\"以获取有关解决此问题的更多信息。

0
0 Comments

问题的原因是在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函数,我们成功地将导入模块的代码在函数中执行,并使得模块在全局作用域中可用。

0
0 Comments

文章标题:如何在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

太棒了!它起作用了!感谢你详细的回答和非常友好的跟进!

0