关于python:如何在给定完整路径的情况下导入模块?

How to import a module given the full path?

如何在给定完整路径的情况下加载Python模块?请注意,该文件可以在文件系统中的任何位置,因为它是一个配置选项。


对于python 3.5+使用:

1
2
3
4
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,请使用:

1
2
3
4
from importlib.machinery import SourceFileLoader

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

(尽管在python 3.4中已经弃用了这种方法。)

Python 2用途:

1
2
3
4
import imp

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

编译后的python文件和dll有相同的方便函数。

请参见。http://bugs.python.org/issue21436。


向sys.path添加路径(而不是使用imp)的好处是,当从单个包导入多个模块时,它简化了操作。例如:

1
2
3
4
5
6
7
import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')

from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch


听起来您不想专门导入配置文件(这涉及到很多副作用和额外的复杂性),您只想运行它,并且能够访问产生的名称空间。标准库以runpy.run_路径的形式专门提供了一个API:

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

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


您也可以这样做,并将配置文件所在的目录添加到python加载路径中,然后进行常规导入,假设您事先知道文件名,在本例中为"config"。

乱七八糟的,但它起作用了。

1
2
3
4
5
6
7
8
configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config


你可以使用

1
load_source(module_name, path_to_file)

来自IMP模块的方法。


我想出了一个稍微修改过的@sebastianrittau的绝妙答案(我认为对于python>3.4),它允许您使用spec_from_loader而不是spec_from_file_location加载扩展名为模块的文件:

1
2
3
4
5
6
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中对路径进行编码的好处是,机器不会试图从扩展名中找出文件的类型。这意味着您可以使用此方法加载类似于.txt文件的内容,但如果不指定加载程序,则无法使用spec_from_file_location,因为.txt不在importlib.machinery.SOURCE_SUFFIXES中。


你是说载入还是导入?

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

1
/foo/bar.py

你可以这样做:

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


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

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

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


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

1
2
3
4
5
6
7
8
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_模块时,它会尝试将顶级uuu in it_uuu.py中的相对导入绑定到顶级模块名称,在本例中是"mymodule"。但"mymodule"尚未加载,因此会出现错误"systemError:父模块"mymodule"未加载,无法执行相对导入"。所以在加载之前需要绑定名称。原因是相对导入系统的基本不变量:"不变量保留是,如果您有sys.modules['spam']和sys.modules['spam.foo'](正如您在上述导入之后所做的那样),后者必须显示为前者的foo属性",如本文所述。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:
        raise ImportError

import_file('/home/somebody/somemodule.py')


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

暂时地

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

永久地

将以下行添加到您的.bashrc文件(在Linux中)和终端中的异常source ~/.bashrc中:

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

信贷/来源:Saarrr,另一个StackExchange问题


我相信您可以使用imp.find_module()imp.load_module()来加载指定的模块。您需要将模块名与路径分开,即,如果您想加载/home/mypath/mymodule.py,则需要执行以下操作:

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

…但这应该能完成任务。


python 3.4的这一部分似乎是极其曲折的理解!然而,在使用ChrisCalloway的代码进行了一些黑客攻击之后,我设法找到了一些有用的东西。这是基本功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

这似乎使用了Python3.4中不推荐使用的模块。我不想假装理解为什么,但它似乎在程序中起作用。我发现Chris的解决方案在命令行上有效,但不是在程序内部。


我并不是说它更好,但为了完整性,我想建议使用在python 2和3中都可用的exec函数。exec允许您在全局范围或作为字典提供的内部范围内执行任意代码。

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

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

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

如果通过属性访问,而不是通过键访问对您很重要,您可以为全局变量设计一个自定义dict类,它提供了这样的访问,例如:

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


创建python模块test.py

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

创建python模块test_check.py

1
2
3
from test import Client1
from test import Client2
from test import test3

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


您可以使用pkgutil模块(特别是walk_packages方法)获取当前目录中的包列表。从那里,使用importlib机器导入您想要的模块很简单:

1
2
3
4
5
6
7
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!

这应该管用

1
2
3
4
5
6
7
path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
    basename = os.path.basename(infile)
    basename_without_extension = basename[:-3]

    # http://docs.python.org/library/imp.html?highlight=imp#module-imp
    imp.load_source(basename_without_extension, infile)


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

1
2
3
4
5
6
7
8
9
10
11
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

运行时导入包模块(python配方)

http://code.activestate.com/recipes/223972/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
###################
##                #
## classloader.py #
##                #
###################

import sys, types

def _get_mod(modulePath):
    try:
        aMod = sys.modules[modulePath]
        if not isinstance(aMod, types.ModuleType):
            raise KeyError
    except KeyError:
        # The last [''] is very important!
        aMod = __import__(modulePath, globals(), locals(), [''])
        sys.modules[modulePath] = aMod
    return aMod

def _get_func(fullFuncName):
   """Retrieve a function object from a full dotted-package name."""

    # Parse out the path, module, and function
    lastDot = fullFuncName.rfind(u".")
    funcName = fullFuncName[lastDot + 1:]
    modPath = fullFuncName[:lastDot]

    aMod = _get_mod(modPath)
    aFunc = getattr(aMod, funcName)

    # Assert that the function is a *callable* attribute.
    assert callable(aFunc), u"%s is not callable." % fullFuncName

    # Return a reference to the function itself,
    # not the results of the function.
    return aFunc

def _get_class(fullClassName, parentClass=None):
   """Load a module and retrieve a class (NOT an instance).

    If the parentClass is supplied, className must be of parentClass
    or a subclass of parentClass (or None is returned).
   """

    aClass = _get_func(fullClassName)

    # Assert that the class is a subclass of parentClass.
    if parentClass is not None:
        if not issubclass(aClass, parentClass):
            raise TypeError(u"%s is not a subclass of %s" %
                            (fullClassName, parentClass))

    # Return a reference to the class itself, not an instantiated object.
    return aClass


######################
##       Usage      ##
######################

class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass

def storage_object(aFullClassName, allOptions={}):
    aStoreClass = _get_class(aFullClassName, StorageManager)
    return aStoreClass(allOptions)


我为你做了一个使用imp的包裹。我称之为import_file,它的使用方式如下:

1
2
3
>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')

您可以在以下网址获得:

http://pypi.python.org/pypi/import_文件

或AT

http://code.google.com/p/import-file(导入文件)/


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

IE:

1
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中包含以下内容

1
from module import *


非常简单的方法:假设您希望导入具有相对路径../../mylibs/pyfunc.py的文件。

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

但如果你没有警卫,你最终会走上一条很长的路。


使用importlib而不是imp包的简单解决方案(针对python 2.7测试,尽管它也适用于python 3):

1
2
3
4
5
6
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")

现在可以直接使用导入模块的名称空间,如下所示:

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

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


因为我找不到任何有效的答案,所以将此添加到答案列表中。这将允许在3.4中导入编译的(pyd)python模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()


这个答案是对塞巴斯蒂安·里陶回答评论的补充:"但是如果你没有模块名呢?"这是一种快速而肮脏的方法,可以将可能的python模块名命名为一个文件名——它只是在树上查找一个没有__init__.py文件的目录,然后将其转换为文件名。对于python 3.4+(使用pathlib),这是有意义的,因为py2用户可以使用"imp"或其他方式进行相对导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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文件,通常情况下,这是个技巧。


我认为,最好的办法是从官方文件(29.1.IMP-访问导入内部):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()