给定完整路径,如何导入模块?


1138

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


21
漂亮而简单的问题-有用的答案,但它们使我想知道python咒语“有一种 明显的方法” 发生了什么。对于这样的基本操作,似乎很荒谬,而且依赖于版本(在较新的版本中,它看起来和肿得多。)
inger

Answers:


1279

对于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


53
如果我知道名称空间-'module.name'-我已经使用了__import__
Sridhar Ratnakumar,2009年

62
@SridharRatnakumar的第一个参数的值imp.load_source仅设置.__name__返回模块的。它不会影响加载。
Dan D.

17
@丹 —的第一个参数imp.load_source()确定在sys.modules字典中创建的新条目的键,因此第一个参数确实会影响加载。
Brandon Rhodes

9
imp自版本3.4起不推荐使用该模块:该imp软件包暂待弃用,而推荐使用importlib
Chiel 10 Brinke

9
@AXO和更重要的是一个奇迹,为什么一些简单和基本的,因为这已经被这么复杂。它并没有很多其他语言。
岩石

422

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

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

13
我们如何使用sys.path.append它指向单个python文件而不是目录?
Phani

28
:-)也许您的问题更适合作为StackOverflow问题,而不是对答案的评论。
Daryl Spitzer 2015年

3
python路径可以包含zip存档,“ eggs”(一种复杂的zip存档)等。可以从其中导入模块。因此,路径元素确实是文件的容器,但不一定是目录。
亚历克西斯2015年

12
当心Python缓存导入语句的事实。在极少数情况下,您有两个不同的文件夹共享一个类名(classX),向sys.path添加路径,导入classX,删除路径并重复剩余路径的方法不起作用。Python将始终从其缓存的第一个路径加载类。就我而言,我旨在创建一个插件系统,其中所有插件都实现特定的classX。我最终使用SourceFileLoader,请注意,它的弃用是有争议的
ComFreek

3
请注意,这种方法允许导入的模块从同一目录中导入其他模块,而模块通常这样做,而接受的答案的方法则不这样做(至少在3.7中如此)。importlib.import_module(mod_name)如果在运行时不知道模块名称,则可以使用代替显式导入sys.path.pop(),但是我会在末尾添加a ,但前提是假设导入的代码在使用时不会尝试导入更多模块。
Eli_B

43

如果您的顶级模块不是文件,而是与__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属性” ,如此处所述


非常感谢!此方法启用子模块之间的相对导入。大!
tebanep

这个答案与此处的文档相匹配:docs.python.org/3/library/…
蒂姆·卢德温斯基

1
但是什么mymodule呢?
Gulzar

@Gulzar,它是您想给模块使用的名称,以便以后可以使用:“ from mymodule import myclass”
Idodo

所以... /path/to/your/module/实际上是/path/to/your/PACKAGE/吗?而mymodule你的意思是myfile.py
Gulzar

37

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

暂时

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

永久性

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

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

信用/来源:saarrrr另一个stackexchange 问题


3
如果要在其他地方的Jupyter笔记本中进行项目生产,则此“临时”解决方案是一个很好的答案。
福地

但是...篡改路径很危险
Shai Alon

@ShaiAlon您正在添加路径,因此除了将代码从一台计算机传输到另一台计算机外,其他任何危险都不会使路径混乱。因此,对于软件包开发,我仅导入本地软件包。另外,程序包名称应唯一。如果您担心,请使用临时解决方案。
Miladiouss

28

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

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

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


我喜欢这种方法,但是当我得到run_path的结果时,它似乎是无法访问的字典?
斯蒂芬·艾尔伍德

“无法访问”是什么意思?你不能从它导入(这就是为什么这只是在没有实际需要进口的方式来访问一个不错的选择),但内容应通过正规字典API(可用result[name]result.get('name', default_value)等等)
ncoghlan

这个答案被低估了。它非常简短!更好的是,如果您需要适当的模块名称空间,则可以执行类似操作 from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo

20

您还可以执行类似的操作,并将配置文件所在的目录添加到Python加载路径中,然后进行常规导入,前提是您事先知道文件名,在本例中为“ config”。

凌乱,但有效。

configfile = '~/config.py'

import os
import sys

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

import config

那不是动态的。
Shai Alon

我尝试过:config_file ='聊天设置',setup_file = get_setup_file(config_file +“ .py”),sys.path.append(os.path.dirname(os.path.expanduser(setup_file))),导入config_file >>“ ImportError:没有名为config_file的模块”
Shai Alon

17

您可以使用

load_source(module_name, path_to_file) 

来自imp模块的方法。


...以及imp.load_dynamic(module_name, path_to_file)DLL
HEKTO

34
抬起头来,小鬼现在已过时。
t1m0 '16

13
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')

37
当标准库已经解决了这行代码时,为什么还要编写14行错误代码?您尚未对full_path_to_module或os.whatever操作的格式或内容进行错误检查;使用包罗万象的except:子句很少是一个好主意。
克里斯·约翰逊

您应该在这里使用更多的“ try-finally”。例如save_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
凯-SE是邪恶的2014年

11
@ChrisJohnson this is already addressed by the standard library是的,但是python有不向后兼容的讨厌习惯……如检查后的答案所说,在3.3之前和之后有2种不同的方式。在那种情况下,我想编写自己的通用函数,而不是即时检查版本。是的,也许这段代码没有受到很好的错误保护,但是它显示了一个主意(这是os.chdir(),我对此还没有了解),可以在此基础上编写更好的代码。因此+1。
Sushi271

13

这是一些适用于所有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())

我测试了 它可能很丑陋,但到目前为止,它是唯一可以在所有版本中使用的版本。


1
这个答案在load_source没有用的地方对我有用,因为它导入脚本并在导入时提供对模块和全局变量的脚本访问。
Klik

13

我想出了@SebastianRittau的一个很好的答案的略微修改的版本(我认为是针对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方式对路径进行编码的优点在于,该机制不会尝试从扩展名中找出文件的类型。这意味着您可以.txt使用此方法加载类似文件的内容,但是如果spec_from_file_location不指定loader,.txt则无法进行加载,因为not in中importlib.machinery.SOURCE_SUFFIXES


13

您是指加载还是导入?

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

/foo/bar.py

您可以这样做:

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

1
@Wheat为什么使用sys.path [0:0]而不是sys.path [0]?
user618677 2012年

5
B / c sys.path [0] = xy覆盖第一个路径项,而path [0:0] = xy等效于path.insert(0,xy)
dom0

2
嗯,path.insert对我有用,但是[0:0]技巧却没有。
jsh

11
sys.path[0:0] = ['/foo']
凯文·爱德华兹

6
Explicit is better than implicit.那么为什么不sys.path.insert(0, ...)代替sys.path[0:0]呢?
Winklerrr

8

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

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

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


6

您可以使用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!

5

创建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

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


4

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中不推荐使用的模块。我不假装理解为什么,但是它似乎可以在程序中运行。我发现克里斯的解决方案在命令行上有效,但不是在程序内部。


4

我并不是说它更好,但是为了完整起见,我想建议该exec函数在python 2和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)

4

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

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

3

这应该工作

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)

4
删除扩展名的更一般方法是:name, ext = os.path.splitext(os.path.basename(infile))。您的方法有效,因为以前对.py扩展名有限制。另外,您可能应该将模块导入到某些变量/字典条目中。
ReneSac

3

如果我们在同一项目中有脚本,但在不同的目录中有脚本,则可以通过以下方法解决此问题。

在这种情况下utils.pysrc/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

2

imp为您准备了一个包装。我称呼它import_file,这就是它的用法:

>>>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_file

http://code.google.com/p/import-file/


1
os.chdir吗?(批准评论的最小字符)。
ychaouche 2012年

我整天都在对pyinstaller生成的exe中的导入错误进行故障排除。最后,这是唯一对我有用的东西。非常感谢您所做的!
frakman1

2

在运行时导入软件包模块(Python配方)

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

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

2

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

即:

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

/absolute/path/to/script/module.pyc如果您更改python 的内容,python将会创建并更新它/absolute/path/to/module/module.py

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

from module import *

1
这是我使用过的hack,它给我带来了一些问题。较痛苦的问题之一是,IDEA存在一个问题,即它不从链接中拾取更改的代码,但试图保存它认为的内容。最后要保存的比赛条件是什么……我因此失去了很多工作。
Gripp 2015年

@Gripp不确定我是否了解您的问题,但是我经常(几乎是排他性地)使用Cyber​​Duck之类的客户端通过SFTP在桌面上的远程服务器上编辑脚本,在这种情况下,尝试并编辑符号链接文件,而不是更安全地编辑原始文件。您可以使用git并检查您git status的脚本,以确认对脚本所做的更改实际上已将其返回到源文档中,并且不会在以太文中丢失,从而可以解决其中的一些问题。
user5359531

2

我已经基于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.43.2.52.7.16

优点

  • 可以将两个模块都作为子模块导入,也可以将模块的内容导入父模块(如果没有父模块,则可以导入全局变量)。
  • 可以导入文件名中带有句点的模块。
  • 可以从任何扩展模块导入任何扩展模块。
  • 可以为子模块使用独立名称,而不是默认情况下不带扩展名的文件名(例如,testlib.std.pyas testlibtestlib.blabla.pyas testlib_blabla等)。
  • 不依赖于sys.path搜索路径存储。
  • 不需要在调用之间SOURCE_FILESOURCE_DIR之间保存/恢复全局变量tkl_import_module
  • [用于3.4.x和较高]可以混合在嵌套的模块命名空间tkl_import_module的呼叫(例如:named->local->namedlocal->named->local此类推)。
  • [ 3.4.x及更高]可以将全局变量/函数/类从声明的位置自动导出到通过tkl_import_module(通过tkl_declare_global函数)导入的所有子模块。

缺点

  • [for 3.3.xand lower]要求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

所有功能都已移入铲斗库,请参见上面的链接。


2

有一个专用于此的软件包

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)上进行了测试,但是根据项目的大小,它可能会显得过大。


1

将其添加到答案列表中,因为我找不到任何有效的方法。这将允许导入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()

1

很简单的方法:假设您要导入具有相对路径../../MyLibs/pyfunc.py的文件


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

但是,如果没有警卫就可以做到,那么您最终可以走很长的路


1

一个简单的解决方案,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

@bgusach这可能是对的,但在某些情况下也是希望的(从单个软件包中导入多个模块时,将路径添加到sys.path可以简化操作)。无论如何,如果这不是所希望的,则可以在此之后立即进行sys.path.pop()
Ataxias '18

0

这个答案是塞巴斯蒂安·里陶(Sebastian Rittau)对评论的回答的补充:“但是,如果您没有模块名怎么办?” 这是一种在给定文件名的情况下获取可能的python模块名称的快速而肮脏的方法-它只是沿树走,直到找到没有__init__.py文件的目录,然后将其转换回文件名。对于Python 3.4+(使用pathlib),这很有意义,因为Py2人可以使用“ 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总的来说,这可以解决问题。


-1

我认为,最好的方法是从官方文档中获取(29.1。imp —访问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()

1
此解决方案不允许您提供路径,而这正是问题的所在。
米卡·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.