【问题标题】:How to import a module given the full path?如何在给定完整路径的情况下导入模块?
【发布时间】:2021-10-04 15:50:31
【问题描述】:

如何在给定完整路径的情况下加载 Python 模块?

请注意,该文件可以位于文件系统中的任何位置,因为它是一个配置选项。

【问题讨论】:

  • 不错的简单问题 - 以及有用的答案,但它们让我想知道 python 口头禅“有 one obvious 方法”会发生什么它..它看起来不像是一个单一的或简单而明显的答案。.对于这样一个基本操作来说,它看起来非常糟糕并且依赖于版本(并且在新版本中它看起来更加臃肿..)。
  • @inger python 口头禅“有一种明显的方法”会发生什么[...] [不是] 一个单一的或简单而明显的答案 [.. .] 可笑的 hacky [...] 在新版本中更加臃肿 欢迎来到可怕的 python 包管理世界。 Python 的import、virtualenv、pip、setuptools 之类的都应该被扔掉并用工作代码代替。我只是想摸索virtualenv 或者是pipenv 并且必须通过相当于Jumbo Jet 手册的工作。这种设计如何被炫耀为与部门打交道的解决方案完全让我无法理解。
  • 相关XKCD xkcd.com/1987
  • @JohnFrazer 不断唠叨那些懒得阅读两段文档的人,情况变得更糟了。您的 XKCD 并不真正相关,因为它显示了这些人在尝试某些事情直到某些事情奏效时可以实现的目标。此外,仅仅因为有一种新方法并不意味着现在有“两种明显的方法”。旧方式在某些情况下很明显,新方式向其他情况介绍了易用性。当您真正关心 DevX 时,就会发生这种情况。
  • 并认为 Java 甚至 PHP(这些天)有清晰而简单的方法来拆分包/命名空间中的内容并重用它。看到 Python 如此痛苦,它在其他各个方面都采用了简单性,这真是令人震惊。

标签: python configuration python-import python-module


【解决方案1】:

要添加到Sebastian Rittau 的答案: 至少对于CPython,有pydoc,虽然没有正式声明,但它的作用是导入文件:

from pydoc import importfile
module = importfile('/path/to/module.py')

PS. 为完整起见,在撰写本文时引用了当前实现:pydoc.py,我很高兴以xkcd 1987 的方式说它没有使用issue 21436 中提到的任何实现——至少,不是逐字记录。

【讨论】:

    【解决方案2】:

    特别之处是使用 Exec() 导入具有绝对路径的模块: (exec 接受一个代码字符串或代码对象。而 eval 接受一个表达式。)

    PYMODULE = 'C:\maXbox\mX47464\maxbox4\examples\histogram15.py';
    Execstring(LoadStringJ(PYMODULE));
    

    然后用eval()获取值或对象:

    println('get module data: '+evalStr('pyplot.hist(x)'));
    

    使用 exec 加载模块就像使用通配符命名空间导入:

    Execstring('sys.path.append(r'+'"'+PYMODULEPATH+'")');
    Execstring('from histogram import *'); 
    

    【讨论】:

      【解决方案3】:

      我发现这是一个简单的答案:

      module = dict()
      
      code = """
      import json
      
      def testhi() :
          return json.dumps({"key" : "value"}, indent = 4 )
      """
      
      exec(code, module)
      x = module['testhi']()
      print(x)
      

      【讨论】:

        【解决方案4】:

        这是一种加载文件的方法,类似于 C 等。

        from importlib.machinery import SourceFileLoader
        import os
        
        def LOAD(MODULE_PATH):
            if (MODULE_PATH[0] == "/"):
                FULL_PATH = MODULE_PATH;
            else:
                DIR_PATH = os.path.dirname (os.path.realpath (__file__))
                FULL_PATH = os.path.normpath (DIR_PATH + "/" + MODULE_PATH)
        
            return SourceFileLoader (FULL_PATH, FULL_PATH).load_module ()
        

        在哪里实现:

        Y = LOAD("../Z.py")
        A = LOAD("./A.py")
        D = LOAD("./C/D.py")
        A_ = LOAD("/IMPORTS/A.py")
        
        Y.DEF();
        A.DEF();
        D.DEF();
        A_.DEF();
        

        每个文件如下所示:

        def DEF():
            print("A");
        

        【讨论】:

          【解决方案5】:

          这是我仅使用 pathlib 的两个实用程序函数。它从路径推断模块名称。

          默认情况下,它会递归地从文件夹中加载所有 Python 文件,并将 init.py 替换为父文件夹名称。但您也可以提供 Path 和/或 glob 来选择某些特定文件。

          from pathlib import Path
          from importlib.util import spec_from_file_location, module_from_spec
          from typing import Optional
          
          
          def get_module_from_path(path: Path, relative_to: Optional[Path] = None):
              if not relative_to:
                  relative_to = Path.cwd()
          
              abs_path = path.absolute()
              relative_path = abs_path.relative_to(relative_to.absolute())
              if relative_path.name == "__init__.py":
                  relative_path = relative_path.parent
              module_name = ".".join(relative_path.with_suffix("").parts)
              mod = module_from_spec(spec_from_file_location(module_name, path))
              return mod
          
          
          def get_modules_from_folder(folder: Optional[Path] = None, glob_str: str = "*/**/*.py"):
              if not folder:
                  folder = Path(".")
          
              mod_list = []
              for file_path in sorted(folder.glob(glob_str)):
                  mod_list.append(get_module_from_path(file_path))
          
              return mod_list
          

          【讨论】:

            【解决方案6】:

            我基于importlib模块编写了自己的全局可移植导入函数,用于:

            • 能够将两个模块作为子模块导入,并将模块的内容导入父模块(如果没有父模块,则导入全局模块)。
            • 能够导入文件名中带有句点字符的模块。
            • 能够导入具有任何扩展名的模块。
            • 能够为子模块使用独立名称,而不是默认情况下不带扩展名的文件名。
            • 能够根据之前导入的模块定义导入顺序,而不是依赖于sys.path 或任何搜索路径存储。

            示例目录结构:

            <root>
             |
             +- test.py
             |
             +- testlib.py
             |
             +- /std1
             |   |
             |   +- testlib.std1.py
             |
             +- /std2
             |   |
             |   +- testlib.std2.py
             |
             +- /std3
                 |
                 +- testlib.std3.py
            

            包含依赖和顺序:

            test.py
              -> testlib.py
                -> testlib.std1.py
                  -> testlib.std2.py
                -> testlib.std3.py
            

            实施:

            最新更改存储:https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

            test.py:

            import os, sys, inspect, copy
            
            SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
            SOURCE_DIR = os.path.dirname(SOURCE_FILE)
            
            print("test::SOURCE_FILE: ", SOURCE_FILE)
            
            # portable import to the global space
            sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
            import tacklelib as tkl
            
            tkl.tkl_init(tkl)
            
            # cleanup
            del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
            sys.path.pop()
            
            tkl_import_module(SOURCE_DIR, 'testlib.py')
            
            print(globals().keys())
            
            testlib.base_test()
            testlib.testlib_std1.std1_test()
            testlib.testlib_std1.testlib_std2.std2_test()
            #testlib.testlib.std3.std3_test()                             # does not reachable directly ...
            getattr(globals()['testlib'], 'testlib.std3').std3_test()     # ... but reachable through the `globals` + `getattr`
            
            tkl_import_module(SOURCE_DIR, 'testlib.py', '.')
            
            print(globals().keys())
            
            base_test()
            testlib_std1.std1_test()
            testlib_std1.testlib_std2.std2_test()
            #testlib.std3.std3_test()                                     # does not reachable directly ...
            globals()['testlib.std3'].std3_test()                         # ... but reachable through the `globals` + `getattr`
            

            testlib.py:

            # optional for 3.4.x and higher
            #import os, inspect
            #
            #SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
            #SOURCE_DIR = os.path.dirname(SOURCE_FILE)
            
            print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)
            
            tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')
            
            # SOURCE_DIR is restored here
            print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)
            
            tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')
            
            print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)
            
            def base_test():
              print('base_test')
            

            testlib.std1.py:

            # optional for 3.4.x and higher
            #import os, inspect
            #
            #SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
            #SOURCE_DIR = os.path.dirname(SOURCE_FILE)
            
            print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)
            
            tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')
            
            def std1_test():
              print('std1_test')
            

            testlib.std2.py:

            # optional for 3.4.x and higher
            #import os, inspect
            #
            #SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
            #SOURCE_DIR = os.path.dirname(SOURCE_FILE)
            
            print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)
            
            def std2_test():
              print('std2_test')
            

            testlib.std3.py:

            # optional for 3.4.x and higher
            #import os, inspect
            #
            #SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
            #SOURCE_DIR = os.path.dirname(SOURCE_FILE)
            
            print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)
            
            def std3_test():
              print('std3_test')
            

            输出 (3.7.4):

            test::SOURCE_FILE:  <root>/test01/test.py
            import : <root>/test01/testlib.py as testlib -> []
            1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
            import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
            import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
            testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
            2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
            import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
            testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
            3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
            dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
            base_test
            std1_test
            std2_test
            std3_test
            import : <root>/test01/testlib.py as . -> []
            1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
            import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
            import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
            testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
            2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
            import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
            testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
            3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
            dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
            base_test
            std1_test
            std2_test
            std3_test
            

            在 Python 中测试3.7.4、3.2.5、2.7.16

            优点:

            • 可以将两个模块作为子模块导入,也可以将模块的内容导入父模块(如果没有父模块,则导入全局)。
            • 可以导入文件名中带有句点的模块。
            • 可以从任何扩展模块导入任何扩展模块。
            • 可以为子模块使用独立名称,而不是默认的不带扩展名的文件名(例如,testlib.std.py 为 testlib,testlib.blabla.py 为 testlib_blabla 等等)。
            • 不依赖于 sys.path 或任何搜索路径存储。
            • 在调用tkl_import_module 之间不需要保存/恢复像SOURCE_FILE 和SOURCE_DIR 这样的全局变量。
            • [对于3.4.x 及更高版本] 可以在嵌套的tkl_import_module 调用中混合模块命名空间(例如:named-&gt;local-&gt;named 或local-&gt;named-&gt;local 等)。
            • [对于3.4.x 及更高版本] 可以自动将全局变量/函数/类从声明的地方导出到通过tkl_import_module(通过tkl_declare_global 函数)导入的所有子模块。

            缺点:

            • [3.3.x 及以下] 要求在所有调用tkl_import_module 的模块中声明tkl_import_module(代码重复)

            更新 1,2(仅适用于 3.4.x 及更高版本):

            在 Python 3.4 及更高版本中,您可以通过在顶级模块中声明 tkl_import_module 来绕过在每个模块中声明 tkl_import_module 的要求,并且该函数将在一次调用中将自身注入所有子模块(这是一种自部署导入)。

            更新 3:

            添加了函数 tkl_source_module 类似于 bash source 并在导入时支持执行保护(通过模块合并而不是导入来实现)。

            更新 4:

            添加了函数 tkl_declare_global 以自动将模块全局变量导出到模块全局变量不可见的所有子模块,因为它不是子模块的一部分。

            更新 5:

            所有函数都移到了土豪库中,见上面的链接。

            【讨论】:

              【解决方案7】:

              创建 Python 模块 test.py:

              import sys
              sys.path.append("<project-path>/lib/")
              from tes1 import Client1
              from tes2 import Client2
              import tes3
              

              创建 Python 模块test_check.py:

              from test import Client1
              from test import Client2
              from test import test3
              

              我们可以从模块中导入导入的模块。

              【讨论】:

                【解决方案8】:

                要导入您的模块,您需要将其目录临时或永久添加到环境变量中。

                暂时

                import sys
                sys.path.append("/path/to/my/modules/")
                import my_module
                

                永久

                将以下行添加到 Linux 中的 .bashrc(或替代)文件中 并在终端中执行source ~/.bashrc(或替代):

                export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"
                

                来源/来源:saarrrr、another Stack Exchange question

                【讨论】:

                • 如果您想在其他地方的 jupyter notebook 中推动项目,这个“临时”解决方案是一个很好的答案。
                • 但是……篡改路径很危险
                • @ShaiAlon 您正在添加路径,因此除了将代码从一台计算机传输到另一台计算机时,没有任何危险,路径可能会混乱。所以,对于包开发,我只导入本地包。此外,包名称应该是唯一的。如果您担心,请使用临时解决方案。
                【解决方案9】:

                这个答案是对Sebastian Rittau's answer回复评论的补充:“但是如果你没有模块名怎么办?”这是获取给定文件名的可能 Python 模块名称的一种快速而肮脏的方法——它只是向上爬,直到找到没有 __init__.py 文件的目录,然后将其转换回文件名。对于 Python 3.4+(使用 pathlib),这是有道理的,因为 Python 2 人们可以使用“imp”或其他方式进行相对导入:

                import pathlib
                
                def likely_python_module(filename):
                    '''
                    Given a filename or Path, return the "likely" python module name.  That is, iterate
                    the parent directories until it doesn't contain an __init__.py file.
                
                    :rtype: str
                    '''
                    p = pathlib.Path(filename).resolve()
                    paths = []
                    if p.name != '__init__.py':
                        paths.append(p.stem)
                    while True:
                        p = p.parent
                        if not p:
                            break
                        if not p.is_dir():
                            break
                
                        inits = [f for f in p.iterdir() if f.name == '__init__.py']
                        if not inits:
                            break
                
                        paths.append(p.stem)
                
                    return '.'.join(reversed(paths))
                

                当然有改进的可能性,可选的__init__.py 文件可能需要进行其他更改,但如果您通常有__init__.py,那么这样做就可以了。

                【讨论】:

                  【解决方案10】:

                  一个非常简单的方法:假设你想要导入具有相对路径的文件 ../../MyLibs/pyfunc.py

                  libPath = '../../MyLibs'
                  import sys
                  if not libPath in sys.path: sys.path.append(libPath)
                  import pyfunc as pf
                  

                  但如果你没有守卫,你最终可以走很长的路。

                  【讨论】:

                    【解决方案11】:

                    这将允许在 3.4 中导入已编译 (pyd) 的 Python 模块:

                    import sys
                    import importlib.machinery
                    
                    def load_module(name, filename):
                        # If the Loader finds the module name in this list it will use
                        # module_name.__file__ instead so we need to delete it here
                        if name in sys.modules:
                            del sys.modules[name]
                        loader = importlib.machinery.ExtensionFileLoader(name, filename)
                        module = loader.load_module()
                        locals()[name] = module
                        globals()[name] = module
                    
                    load_module('something', r'C:\Path\To\something.pyd')
                    something.do_something()
                    

                    【讨论】:

                      【解决方案12】:

                      这里有一些适用于所有 Python 版本的代码,从 2.7 到 3.5,甚至可能还有其他版本。

                      config_file = "/tmp/config.py"
                      with open(config_file) as f:
                          code = compile(f.read(), config_file, 'exec')
                          exec(code, globals(), locals())
                      

                      我测试过了。它可能很丑陋,但到目前为止,它是唯一适用于所有版本的。

                      【讨论】:

                      • 这个答案对我有用,load_source 没有,因为它导入脚本并在导入时提供对模块和全局变量的脚本访问。
                      • 请注意,此答案的行为与导入模块不同,至于模块(是否以正常方式导入)代码的“全局”范围是模块对象,而对于这个答案,它是被调用对象的全局范围。 (虽然这个答案也可以修改以改变范围,任何字典都可以作为globals和locals传入)
                      【解决方案13】:

                      听起来您不想专门导入配置文件(这会带来很多副作用和其他复杂情况)。您只想运行它,并能够访问生成的命名空间。标准库以runpy.run_path的形式提供了专门的API:

                      from runpy import run_path
                      settings = run_path("/path/to/file.py")
                      

                      该接口在 Python 2.7 和 Python 3.2+ 中可用。

                      【讨论】:

                      • 我喜欢这种方法,但是当我得到 run_path 的结果时,它是一个我似乎无法访问的字典?
                      • “无法访问”是什么意思?您不能从中导入(这就是为什么这只是在实际上不需要导入样式访问时这是一个不错的选择),但内容应该可以通过常规 dict API 获得(result[name]、result.get('name', default_value) 等)
                      • @Maggyero 命令行永远不会通过runpy.run_path,但如果给定路径是目录或压缩文件,那么它最终会委托给runpy.run_module 以执行__main__。 “它是脚本、目录还是 zip 文件?”的重复逻辑还不够复杂,不值得委派给 Python 代码。
                      • 另外,通过查看 C 函数 pymain_run_module 的 implementation,似乎 CPython 委托给 Python 函数 runpy._run_module_as_main 而不是 runpy.run_module——尽管如果我理解正确的话,唯一的区别是不是第一个函数在内置的__main__ 环境中执行代码(参见here),而第二个函数在新环境中执行?
                      • @Maggyero 是的,这是唯一的区别。最初它使用公共函数,但结果与解释器的 -i 选项交互不良(这会将您放入原始 __main__ 模块中的交互式 shell,因此在新模块中运行 -m 很不方便)
                      【解决方案14】:

                      我并不是说它更好,但为了完整起见,我想推荐 exec 函数,它在 Python 2 和 Python 3 中都可用。

                      exec 允许您在以字典形式提供的全局范围或内部范围内执行任意代码。

                      例如,如果您有一个存储在 "/path/to/module" 中的模块,其函数为 foo(),您可以通过执行以下操作来运行它:

                      module = dict()
                      with open("/path/to/module") as f:
                          exec(f.read(), module)
                      module['foo']()
                      

                      这使您动态加载代码更加明确,并赋予您一些额外的能力,例如提供自定义内置函数的能力。

                      如果通过属性而不是键访问对您来说很重要,您可以为全局变量设计一个自定义 dict 类,以提供此类访问权限,例如:

                      class MyModuleClass(dict):
                          def __getattr__(self, name):
                              return self.__getitem__(name)
                      

                      【讨论】:

                        【解决方案15】:

                        在 Linux 中,在 Python 脚本所在的目录中添加符号链接是可行的。

                        即:

                        ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py
                        

                        Python 解释器将创建/absolute/path/to/script/module.pyc,并在您更改/absolute/path/to/module/module.py 的内容时更新它。

                        然后在文件 mypythonscript.py 中包含以下内容:

                        from module import *
                        

                        【讨论】:

                        • 这是我使用的hack,它给我带来了一些问题。其中一个更痛苦的问题是,IDEA 存在一个问题,即它不会从链接中获取更改的代码,但会尝试保存它认为存在的内容。最后一个拯救的比赛条件就是坚持......因此我失去了相当多的工作。
                        • @Gripp 不确定我是否理解您的问题,但我经常(几乎完全)使用 Cyber​​Duck 之类的客户端通过 SFTP 从我的桌面在远程服务器上编辑我的脚本,在这种情况下也是如此尝试编辑符号链接文件不是一个好主意,而是编辑原始文件更安全。您可以通过使用git 并检查您的git status 来发现其中的一些问题,以验证您对脚本的更改实际上是在将其返回到源文档,而不是迷失在以太中。
                        【解决方案16】:

                        您可以使用__import__ 和chdir 来做到这一点:

                        def import_file(full_path_to_module):
                            try:
                                import os
                                module_dir, module_file = os.path.split(full_path_to_module)
                                module_name, module_ext = os.path.splitext(module_file)
                                save_cwd = os.getcwd()
                                os.chdir(module_dir)
                                module_obj = __import__(module_name)
                                module_obj.__file__ = full_path_to_module
                                globals()[module_name] = module_obj
                                os.chdir(save_cwd)
                            except Exception as e:
                                raise ImportError(e)
                            return module_obj
                        
                        
                        import_file('/home/somebody/somemodule.py')
                        

                        【讨论】:

                        • 既然标准库已经解决了这个问题,为什么还要编写 14 行错误代码?您尚未对 full_path_to_module 或 os.whatever 操作的格式或内容进行错误检查;并且使用包罗万象的except: 子句很少是一个好主意。
                        • 你应该在这里使用更多的“try-finally”。例如。 save_cwd = os.getcwd()try: …finally: os.chdir(save_cwd)
                        • @ChrisJohnson this is already addressed by the standard library 是的,但是python有不向后兼容的讨厌习惯......因为检查的答案说在3.3之前和之后有2种不同的方式。在那种情况下,我宁愿编写自己的通用函数,也不愿即时检查版本。是的,也许这段代码没有很好的错误保护,但它显示了一个想法(它是 os.chdir(),我还没想过),基于它我可以编写更好的代码。因此 +1。
                        • 如果这真的返回了模块,那就太酷了。
                        【解决方案17】:

                        你的意思是加载还是导入?

                        您可以操纵sys.path 列表指定您的模块的路径,然后导入您的模块。例如,给定一个模块:

                        /foo/bar.py
                        

                        你可以这样做:

                        import sys
                        sys.path[0:0] = ['/foo'] # Puts the /foo directory at the start of your path
                        import bar
                        

                        【讨论】:

                        • B/c sys.path[0] = xy 覆盖第一个路径项,而 path[0:0] =xy 等价于 path.insert(0, xy)
                        • hm path.insert 对我有用,但 [0:0] 技巧没有。
                        • sys.path[0:0] = ['/foo']
                        • Explicit is better than implicit. 那么为什么不用sys.path.insert(0, ...) 而不是sys.path[0:0]呢?
                        • @dom0 那就用sys.path.append(...)吧。更清楚了。
                        【解决方案18】:

                        你可以使用

                        load_source(module_name, path_to_file)
                        

                        来自imp module的方法。

                        【讨论】:

                        • ... 和 imp.load_dynamic(module_name, path_to_file) 用于 DLL
                        • 提醒 imp 现已弃用。
                        【解决方案19】:

                        如果我们有脚本在同一个项目,但在不同的目录意味着,我们可以通过以下方法解决这个问题。

                        在这种情况下,utils.py 在src/main/util/ 中

                        import sys
                        sys.path.append('./')
                        
                        import src.main.util.utils
                        #or
                        from src.main.util.utils import json_converter # json_converter is example method
                        

                        【讨论】:

                        • 最简单的IMO
                        【解决方案20】:

                        有一个专门用于此的package:

                        from thesmuggler import smuggle
                        
                        # À la `import weapons`
                        weapons = smuggle('weapons.py')
                        
                        # À la `from contraband import drugs, alcohol`
                        drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')
                        
                        # À la `from contraband import drugs as dope, alcohol as booze`
                        dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')
                        

                        它已在 Python 版本(Jython 和 PyPy 也一样)上进行了测试,但根据项目的大小,它可能有点过头了。

                        【讨论】:

                          【解决方案21】:

                          对于 Python 3.5+ 使用:

                          import importlib.util
                          spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
                          foo = importlib.util.module_from_spec(spec)
                          spec.loader.exec_module(foo)
                          foo.MyClass()
                          

                          对于 Python 3.3 和 3.4 使用:

                          from importlib.machinery import SourceFileLoader
                          
                          foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
                          foo.MyClass()
                          

                          (尽管这在 Python 3.4 中已被弃用。)

                          对于 Python 2 使用:

                          import imp
                          
                          foo = imp.load_source('module.name', '/path/to/file.py')
                          foo.MyClass()
                          

                          对于已编译的 Python 文件和 DLL,有等效的便利函数。

                          另见http://bugs.python.org/issue21436。

                          【讨论】:

                          • 如果我知道命名空间 - 'module.name' - 我会使用 __import__。
                          • @SridharRatnakumar imp.load_source 的第一个参数的值仅设置返回模块的.__name__。它不会影响加载。
                          • @DanD。 — imp.load_source() 的第一个参数决定了在 sys.modules 字典中创建的新条目的键,所以第一个参数确实会影响加载。
                          • @AXO 甚至更多,人们想知道为什么像这个 这么简单和基本的东西会如此复杂。它没有许多其他语言。
                          • @Mahesha999 因为 importlib.import_module() 不允许您按文件名导入模块,这是最初的问题。
                          【解决方案22】:

                          如果您的顶级模块不是文件而是使用 __init__.py 打包为目录,那么可接受的解决方案几乎可以工作,但并不完全。在 Python 3.5+ 中需要以下代码(注意添加的以 'sys.modules' 开头的行):

                          MODULE_PATH = "/path/to/your/module/__init__.py"
                          MODULE_NAME = "mymodule"
                          import importlib
                          import sys
                          spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
                          module = importlib.util.module_from_spec(spec)
                          sys.modules[spec.name] = module 
                          spec.loader.exec_module(module)
                          

                          如果没有这一行,当执行 exec_module 时,它​​会尝试将顶级 __init__.py 中的相对导入绑定到顶级模块名称——在本例中为“mymodule”。但是“mymodule”尚未加载,因此您将收到错误“SystemError:未加载父模块'mymodule',无法执行相对导入”。所以你需要在加载之前绑定名称。这样做的原因是相对导入系统的基本不变量:“保持不变的是,如果你有 sys.modules['spam'] 和 sys.modules['spam.foo'] (就像你在上面的导入之后一样),后者必须作为前者的 foo 属性出现"as discussed here。

                          【讨论】:

                          • 非常感谢!此方法启用子模块之间的相对导入。太好了!
                          • 此答案与此处的文档相匹配:docs.python.org/3/library/…。
                          • 但是mymodule 是什么?
                          • @Gulzar,这是你想给你的模块起的任何名字,这样你以后可以这样做:“from mymodule import myclass”
                          • 虽然非常规,但如果您的包入口点不是__init__.py,您仍然可以将其作为包导入。在创建规范后包含spec.submodule_search_locations = [os.path.dirname(MODULE_PATH)]。您还可以通过将此值设置为 None 将 __init__.py 视为非包(例如单个模块)
                          【解决方案23】:

                          我想出了一个稍微修改过的@SebastianRittau's wonderful answer 版本(我认为是Python > 3.4),这将允许您使用spec_from_loader 而不是spec_from_file_location 将具有任何扩展名的文件作为模块加载:

                          from importlib.util import spec_from_loader, module_from_spec
                          from importlib.machinery import SourceFileLoader 
                          
                          spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
                          mod = module_from_spec(spec)
                          spec.loader.exec_module(mod)
                          

                          在显式SourceFileLoader 中编码路径的优点是machinery 不会尝试从扩展名中找出文件的类型。这意味着您可以使用此方法加载类似于.txt 文件的内容,但您无法在不指定加载程序的情况下使用spec_from_file_location 执行此操作,因为.txt 不在importlib.machinery.SOURCE_SUFFIXES 中。

                          【讨论】:

                            【解决方案24】:

                            使用importlib 而不是imp 包的简单解决方案(已针对Python 2.7 进行了测试,尽管它也应该适用于Python 3):

                            import importlib
                            
                            dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
                            sys.path.append(dirname) # only directories should be added to PYTHONPATH
                            module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
                            module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")
                            

                            现在可以直接使用导入模块的命名空间了,像这样:

                            a = module.myvar
                            b = module.myfunc(a)
                            

                            这种解决方案的优点是我们甚至不需要知道我们想要导入的模块的实际名称,就可以在我们的代码中使用它。这很有用,例如如果模块的路径是可配置的参数。

                            【讨论】:

                            • 这样你正在修改sys.path,它并不适合所有用例。
                            • @bgusach 这可能是真的,但在某些情况下它也是可取的(当从一个包中导入多个模块时,添加一个到 sys.path 的路径可以简化事情)。无论如何,如果这不是可取的,可以立即执行sys.path.pop()
                            【解决方案25】:

                            要从给定的文件名导入模块,您可以临时扩展路径,并在 finally 块中恢复系统路径reference:

                            filename = "directory/module.py"
                            
                            directory, module_name = os.path.split(filename)
                            module_name = os.path.splitext(module_name)[0]
                            
                            path = list(sys.path)
                            sys.path.insert(0, directory)
                            try:
                                module = __import__(module_name)
                            finally:
                                sys.path[:] = path # restore
                            

                            【讨论】:

                              【解决方案26】:

                              Python 3.4 的这个领域似乎很难理解!然而,在开始使用 Chris Calloway 的代码进行一些黑客攻击后,我设法得到了一些工作。这是基本功能。

                              def import_module_from_file(full_path_to_module):
                                  """
                                  Import a module given the full path/filename of the .py file
                              
                                  Python 3.4
                              
                                  """
                              
                                  module = None
                              
                                  try:
                              
                                      # Get module name and path from full path
                                      module_dir, module_file = os.path.split(full_path_to_module)
                                      module_name, module_ext = os.path.splitext(module_file)
                              
                                      # Get module "spec" from filename
                                      spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)
                              
                                      module = spec.loader.load_module()
                              
                                  except Exception as ec:
                                      # Simple error printing
                                      # Insert "sophisticated" stuff here
                                      print(ec)
                              
                                  finally:
                                      return module
                              

                              这似乎使用了 Python 3.4 中未弃用的模块。我不假装理解为什么,但它似乎在程序中起作用。我发现 Chris 的解决方案在命令行上有效,但在程序内部无效。

                              【讨论】:

                                【解决方案27】:

                                相信你可以使用imp.find_module()和imp.load_module()来加载指定的模块。您需要将模块名称从路径中拆分出来,即,如果您想加载 /home/mypath/mymodule.py,您需要这样做:

                                imp.find_module('mymodule', '/home/mypath/')
                                

                                ...但这应该可以完成工作。

                                【讨论】:

                                  【解决方案28】:

                                  您可以使用pkgutil 模块(特别是walk_packages 方法)来获取当前目录中的包列表。从那里使用importlib 机器导入你想要的模块是微不足道的:

                                  import pkgutil
                                  import importlib
                                  
                                  packages = pkgutil.walk_packages(path='.')
                                  for importer, name, is_package in packages:
                                      mod = importlib.import_module(name)
                                      # do whatever you want with module now, it's been imported!
                                  

                                  【讨论】:

                                    【解决方案29】:

                                    我认为最好的方法是来自官方文档(29.1. imp — Access the import internals):

                                    import imp
                                    import sys
                                    
                                    def __import__(name, globals=None, locals=None, fromlist=None):
                                        # Fast path: see if the module has already been imported.
                                        try:
                                            return sys.modules[name]
                                        except KeyError:
                                            pass
                                    
                                        # If any of the following calls raises an exception,
                                        # there's a problem we can't handle -- let the caller handle it.
                                    
                                        fp, pathname, description = imp.find_module(name)
                                    
                                        try:
                                            return imp.load_module(name, fp, pathname, description)
                                        finally:
                                            # Since we may exit via an exception, close fp explicitly.
                                            if fp:
                                                fp.close()
                                    

                                    【讨论】:

                                    • 此解决方案不允许您提供路径,这是问题所要求的。
                                    【解决方案30】:

                                    你也可以这样做,将配置文件所在的目录添加到 Python 加载路径中,然后进行正常的导入,假设你事先知道文件的名称,在这种情况下“配置”。

                                    乱七八糟,但它有效。

                                    configfile = '~/config.py'
                                    
                                    import os
                                    import sys
                                    
                                    sys.path.append(os.path.dirname(os.path.expanduser(configfile)))
                                    
                                    import config
                                    

                                    【讨论】:

                                    • 那不是动态的。
                                    • 我试过了:config_file = 'setup-for-chats', setup_file = get_setup_file(config_file + ".py"), sys.path.append(os.path.dirname(os.path.expanduser (setup_file))),导入 config_file >> "ImportError: No module named config_file"
                                    猜你喜欢
                                    • 2022-11-30
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2017-06-11
                                    • 2016-11-20
                                    相关资源
                                    最近更新 更多