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

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

推荐阅读

    配置网关的命令linux?

    配置网关的命令linux?,地址,系统,网络,代码,信息,环境,命令,网关,服务,电脑,

    linux配置ip命令行?

    linux配置ip命令行?,地址,系统,代码,网络,设备,工具,服务,密码,命令,网卡,lin

    linux配置文件夹命令?

    linux配置文件夹命令?,地址,系统,工作,信息,命令,标准,服务,名称,位置,文件,l

    查询linux配置的命令?

    查询linux配置的命令?,系统,网络,地址,情况,信息,电脑,中科,状态,服务,命令,

    查询linux配置的命令?

    查询linux配置的命令?,系统,网络,地址,情况,信息,电脑,中科,状态,服务,命令,

    linux命令重命名配置?

    linux命令重命名配置?,图片,名称,名字,文件,软件,代码,命令,文件名,脚本,批

    linux配置双ip命令?

    linux配置双ip命令?,地址,系统,代码,网络,设备,信息,中心,电脑,密码,命令,Lin

    linux配置自定义命令?

    linux配置自定义命令?,服务,系统,状态,策略,周期,地方,标准,新增,环境,工具,L

    linux设备加载命令?

    linux设备加载命令?,系统,设备,信息,电脑,第一,命令,文件,分区,磁盘,详细信

    linux常用的命令路径?

    linux常用的命令路径?,地址,工作,基础,网络,管理,系统,信息,命令,企业,服务,l

    linux切换路径命令行?

    linux切换路径命令行?,系统,工作,名称,地址,目录,命令,工具,密码,一致,管理,

    linux双网卡配置命令?

    linux双网卡配置命令?,网络,状态,地址,信息,通信,标准,通讯,对外,机电,环境,L

    linux网卡加载命令行?

    linux网卡加载命令行?,系统,地址,代码,网络,设备,电脑,名称,网卡,技术指标,

    linux命令配置串口?

    linux命令配置串口?,设备,系统,平台,工具,名字,通信,电脑,地址,项目,信息,Lin

    linux命令刷新加载?

    linux命令刷新加载?,系统,下来,工具,网上,命令,终端,环境变量,以下,文件,路

    linux改变命令的路径?

    linux改变命令的路径?,数字,第一,目录,工作,一致,名称,系统,命令,文件夹,代

    linux中显示路径命令?

    linux中显示路径命令?,系统,地址,工作,信息,时间,命令,数据,网络,路径,名字,l

    linux保存路径命令行?

    linux保存路径命令行?,状态,地址,电脑,档案,系统,底部,软件,密码,命令,文件,l

    linux的配置网络命令?

    linux的配置网络命令?,地址,系统,网络,代码,服务,管理,密码,信息,基础,命令,l

    linux相对路径命令?

    linux相对路径命令?,工作,名字,路径,目录,管理,信息,系统,位置,名称,基础,lin