【问题标题】:Is this correct way to import python scripts residing in arbitrary folders?这是导入驻留在任意文件夹中的python脚本的正确方法吗?
【发布时间】:2023-03-03 03:47:01
【问题描述】:

此 sn-p 来自 SO 上的较早答案。它大约有一年的历史(答案未被接受)。我是 Python 新手,我发现系统路径真的很痛苦。我在不同的目录中有一些用脚本编写的函数,我希望能够将它们导入到新项目中,而不必费劲。

这是sn-p:

def import_path(fullpath):
""" Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
"""
path, filename = os.path.split(fullpath)
filename, ext = os.path.splitext(filename)
sys.path.append(path)
module = __import__(filename)
reload(module) # Might be out of date
del sys.path[-1]
return module

它来自这里: How to do relative imports in Python?

我想要一些关于我是否可以使用它的反馈 - 以及是否有任何对新手来说可能不明显的不良副作用。

我打算像这样使用它:

import_path(/home/pydev/path1/script1.py)

script1.func1()

等

以我想要的方式使用该功能是否“安全”?

【问题讨论】:

    标签: python


    【解决方案1】:

    “官方”且完全安全的方法是标准 Python 库的 imp 模块。

    使用imp.find_module在您精确指定的可接受目录列表中查找模块——它返回一个三元组(file, pathname, description)——如果不成功,file实际上是None(但它也可以引发ImportError 所以你应该使用try/except 以及检查if file is None:)。

    如果搜索成功,调用imp.load_module(在try/finally 中确保您关闭文件!)在第一个参数后面加上以上三个参数,这三个参数必须与您的name 相同传递给find_module——它返回模块对象(呸;-)。

    【讨论】:

    • 在 3.3 中,imp 已被弃用,取而代之的是 importlib。但是,在 3.2 中,似乎没有任何等效的 importlib 用法,因此,如果您需要在两种环境中运行,请不要头疼并坚持使用 imp。
    【解决方案2】:

    如上所述,如果合适,请考虑线程安全。我更喜欢类似帖子中发布的解决方案。主要区别如下:使用 insert 指定导入的优先级,使用 try...finally 正确恢复 sys.path,以及设置全局命名空间。

    # inspired by Alex Martelli's solution to
    # http://stackoverflow.com/questions/1096216/override-namespace-in-python/1096247#1096247
    def import_from_absolute_path(fullpath, global_name=None):
        """Dynamic script import using full path."""
        import os
        import sys
    
        script_dir, filename = os.path.split(fullpath)
        script, ext = os.path.splitext(filename)
    
        sys.path.insert(0, script_dir)
        try:
            module = __import__(script)
            if global_name is None:
                global_name = script
            globals()[global_name] = module
            sys.modules[global_name] = module
        finally:
            del sys.path[0]
    

    【讨论】:

      【解决方案3】:

      确实感觉有点像 hack,但目前,我想不出任何可能发生的意外副作用,至少只要你只是将它用于你自己的脚本.基本上,它所做的是将指定文件的父目录(在您的示例中,/home/pydev/path1/)临时添加到 Python 在查找要导入的模块时检查的路径列表中。

      我现在能想到的唯一风险将出现在多线程环境中,其中两个或多个线程(或进程)同时运行此功能。如果线程 A 想要从路径 dirA/A.py 导入模块 A,而线程 B 想要从路径 dirB/B.py 导入模块 B,那么您将在短时间内在 sys.path 中同时使用 dirA 和 dirB .如果dirA 中有一个名为B.py 的文件,那么线程B 可能会找到它(dirA/B.py)而不是它正在寻找的文件(dirB/B.py),从而导入了错误的模块。出于这个原因,我不会在生产代码或您要分发给其他人的代码中使用它(至少不会在没有警告他们这个黑客在这里!)的情况下使用它。在这种情况下,您可以编写一个更复杂的函数,允许您指定要导入的文件,而不会弄乱标准路径集。 (例如,这就是 mod_python 所做的)

      【讨论】:

        【解决方案4】:

        我担心您的脚本名称可能与路径中较早出现的模块相对应。为了消除这种恐惧,我会用一个仅包含包含模块的目录的新列表完全替换路径,然后在导入完成后将其放回原处。此外,您应该将其包裹在某种锁中,以便尝试执行相同操作的多个线程不会相互干扰。

        【讨论】:

          猜你喜欢
          • 2018-05-24
          • 2011-06-17
          • 2020-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-26
          • 2017-06-25
          相关资源
          最近更新 更多