如何在Python函数中使用exec(“import xxx”)创建模块?

时间:2015-02-12 15:19:02

标签: python python-2.7

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

anyimport('from fun_abc import *')

最初我试过了:

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

当我在iPython Notebook中编写整个内容时,它可以正常工作。所以这个问题让我失望了。然后我发现它无法正常工作,因为函数导入的模块保留在函数的局部变量空间中。

然后我发现了这个Stack Overflow讨论"In Python, why doesn't an import in an exec in a function work?"。因此我将代码更改为以下内容:

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())

它仍然不起作用。该函数运行时没有错误,但我无法使用这些模块,比如说我运行script.py anyimport('from fun_abc import *')fun_abc中没有任何内容。 Python会告诉我“NameError:name'fun_you_want'未定义”。

有人会非常友好地指出我如何解决这个问题的正确方向吗?

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

注意:

除了@Noya的现场答案,必须通过范围使exec工作,以避免“ImportError”,您需要在运行exec之前添加此行:< / p>

sys.path.append(os.path.join(os.path.dirname(__file__), pointdir))        
exec (evalstr, scope)

这是因为我们对sys.path的修改假定当前工作目录始终位于main/。我们需要将父目录添加到sys.path。有关解决此问题的详细信息,请参阅此Stack Overflow讨论"ImportError: No module named - Python"

2 个答案:

答案 0 :(得分:4)

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提供的导入实用程序。

以下摘自https://stackoverflow.com/a/4526709/453074,相当于from some.package import *

  

[...]用户importlib更方便:

globals().update(importlib.import_module('some.package').__dict__) 

答案 1 :(得分:2)

你可能想尝试这样的事情:

_globals = {}
code = """import math;"""
code += """import numpy;"""
code = compile(code, '<string>', 'exec')
exec code in _globals

它比仅仅exec更安全,并且应该在函数的本地范围内正确导入。

然后,您可以使用导入的任何模块(或函数)更新globals()

使用exec功能时,您可以使用globals获取g = globals()的句柄,然后对g进行更新。对于模块,您应该再做一步......您还需要更新sys.modules中的模块。

更新:明确:

>>> def foo(import_string):
...   _globals = {}
...   code = compile(import_string, '<string>', 'exec')
...   exec code in _globals
...   import sys
...   g = globals()
...   g.update(_globals)
...   sys.modules.update(_globals)
... 
>>> foo('import numpy as np')
>>> np.linspace
<function linspace at 0x1009fc848>