如何在python函数中通过exec(“ import xxx”)使模块可用?

Har*_*wel 3 python python-2.7

我为自己编写了一个简单的函数,该函数可以筛选Python文件夹并查找可能的模块在哪里。我想做的很简单。我传递了一个模块导入字符串,该函数将在其中找到模块的文件夹cd,然后将其导入到我正在使用的任何环境中,例如:

anyimport('from fun_abc import *')
Run Code Online (Sandbox Code Playgroud)

最初我尝试过:

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
Run Code Online (Sandbox Code Playgroud)

当我在iPython Notebook中编写整个代码时,它就可以工作。所以问题就由我溜走了。然后我发现它不能正常工作,因为函数导入的模块保留在函数的局部变量空间中。

然后,我发现了这个Stack Overflow讨论:“在Python中,为什么在函数中的exec中导入无效?” 。因此,我将代码更改为以下内容:

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())
Run Code Online (Sandbox Code Playgroud)

它仍然不起作用。该函数运行没有错误,但是模块对我不可用,比如说如果我script.py在其中运行anyimport('from fun_abc import *')fun_abc那里什么也没有。Python会告诉我“ NameError:名称'fun_you_want'未定义”。

有人会友好地为我指出正确的方向,以解决这个问题吗?

感谢您的关注,非常感谢您的帮助!

注意:

除了@Noya的即时回答(必须通过作用域才能完成exec工作)之外,为了避免出现“ ImportError”,还需要在运行之前添加以下行exec

sys.path.append(os.path.join(os.path.dirname(__file__), pointdir))        
exec (evalstr, scope)
Run Code Online (Sandbox Code Playgroud)

这是由于以下原因,我们对的修改sys.path假设当前工作目录始终在中main/。我们需要将父目录添加到中sys.path。有关解决此问题的更多信息,请参见此 Stack Overflow讨论“ ImportError:未命名的模块-Python”

小智 5

exec在当前范围内执行代码。在函数内部,这意味着(函数)局部作用域。

您可以exec通过给它一个元组来告诉将变量放在另一个作用域中(code, scope)。例如,您可以用来globals()在模块级别提供名称。

请注意,全局变量

始终是当前模块的字典(在函数或方法中,这是定义它的模块,而不是从中调用它的模块)。

因此,在您的示例中,您必须将所需的范围传递给实用程序函数:

anyimport.py

class anyimport(object):
    def __init__(self, importmodule, scope):
        exec (importmodule, scope)
Run Code Online (Sandbox Code Playgroud)

test.py

a = 42
b = 'foo'
Run Code Online (Sandbox Code Playgroud)

main.py

from anyimport import anyimport

if __name__ == '__main__':
    anyimport('from test import *', globals())
    # 42 foo
    print a, b
Run Code Online (Sandbox Code Playgroud)

用进行测试python main.py。确保所有文件都存在于当前目录中。

替代解决方案

如果您不打算使用exec,则一种更优雅的方法是使用Python提供的导入实用程序。

来自/sf/answers/316869661/的以下内容等效于from some.package import *

[...]对用户来说更方便importlib

globals().update(importlib.import_module('some.package').__dict__) 
Run Code Online (Sandbox Code Playgroud)