先入为主
背景
一个函数运行需要根据不同项目的配置,动态导入对应的配置文件运行。
解决
文件结构
a #文件夹
│a.py
│__init__.py
b #文件夹
│b.py
│__init__.py
├─c#文件夹
│c.py
│__init__.py
# c.py 中内容
args = {'a':1}
class C:
def c(self):
pass
目的
向a模块中导入c.py 中的对象
解决方案
import importlib
params = importlib.import_module('b.c.c') #绝对导入
params_ = importlib.import_module('.c.c',package='b') #相对导入
# 对象中取出需要的对象
params.args #取出变量
params.C #取出class C
params.C.c #取出class C 中的c 方法
以上就是动态函数import_module的使用方法
又如:
https://blog.csdn.net/mouday/article/details/84062182
Python编程:importlib.import_module动态导入模块
环境:python 3.6
文件结构
├── clazz
│ ├── __init__.py
│ ├── a.py
│ └── b.py
└── main.py
a.py 的代码
def show():
print("show A")
b.py 的代码
def show():
print("show B")
从main中导入clazz包中的a 和b 模块
import importlib
# 绝对导入
a = importlib.import_module("clazz.a")
a.show()
# show A
# 相对导入
b = importlib.import_module(".b", "clazz")
b.show()
# show B
注意,相对导入有个一点., 类似路径。
一般来说,只有导入过,使用绝对导入或者直接导入都行,相对导入 b = importlib.import_module(".b", "clazz")
才能生效。
更进一步:
Python -V,显示版本号,这里的V是大写的。
https://blog.csdn.net/defending/article/details/78095402
importlib
包的目的是双重的。一个是在Python源代码中提供import
语句(以及扩展名为__import__()
函数)的实现。这提供了可以移植到任何Python解释器的import
的实现。这也提供了比在除了Python之外的编程语言中实现的实现更容易理解的实现。
第二个目的是,实现import
的组件在此包中公开,使用户更容易创建自己的自定义对象(通常称为importer)以参与导入处理。
importlib.__import__(name, globals=None, locals=None, fromlist=(), level=0)
内建__import__()
函数的实现。
注意
程序化导入模块应使用import_module()
而不是此函数。
importlib.import_module(name, package=None)
导入模块。name参数指定要以绝对或相对术语导入的模块。pkg.mod或…mod)。如果名称是以相对术语指定的,则包参数必须设置为包的名称,该名称将作为解析包名称的锚点。import_module(’… mod’, ‘pkg.subpkg’)将导入pkg.mod)。
import_module()
函数用作importlib.__import__()
的简化包装。这意味着函数的所有语义都派生自importlib.import()。这两个函数最重要的区别是import_module()
返回指定的包或模块(例如,pkg.mod),而__import__()
返回顶级包或模块pkg)。
如果您动态导入自解析器开始执行后创建的模块(例如,创建了一个Python源文件),则可能需要调用invalidate_caches()
才能注意到新模块由进口系统。
在版本3.3中更改:父包会自动导入。
importlib.find_loader(name, path=None)
找到模块的加载器,可选择在指定的路径中。如果模块在sys.modules
中,则返回sys.modules[name].__loader__
(除非加载器None未设置,在这种情况下会引发ValueError
)。否则,使用sys.meta_path
的搜索完成。如果未找到加载程序,则返回None。
点名称没有父级的隐式导入,因为需要加载它们,这可能不是所希望的。要正确导入子模块,您需要导入子模块的所有父包,并使用正确的参数path。
版本3.3中的新功能。
在版本3.4中更改:如果__loader__
未设置,则引发ValueError
,就像属性设置为None
自版本3.4后弃用:改用importlib.util.find_spec()
。
importlib.invalidate_caches()
使存储在sys.meta_path
的finder的内部缓存无效。如果finder实现invalidate_caches()
,那么它将被调用以执行无效。如果在程序运行时创建/安装了任何模块,则应调用此函数,以确保所有查找程序都注意到新模块的存在。
版本3.3中的新功能。
importlib.reload(module)
重新载入先前导入的模块。参数必须是一个模块对象,因此必须先被成功导入。如果您已使用外部编辑器编辑了模块源文件,并希望在不离开Python解释器的情况下尝试新版本,这将非常有用。返回值是模块对象(如果重新导入会导致将不同的对象放置在sys.modules
中,则可能不同)。
当执行reload()
时:
Python模块的代码被重新编译并重新执行模块级代码,通过重用最初加载模块的loader
来定义一组新的对象,这些对象被绑定到模块字典中的名称。扩展模块的init功能不再第二次调用。
与Python中的所有其他对象一样,旧对象只有在引用计数下降到零后才被回收。
模块命名空间中的名称将更新为指向任何新的或已更改的对象。
对旧对象的其他引用(例如模块外部的名称)不会重新引用来引用新对象,如果需要,必须在每个命名空间中对其进行更新。
还有一些其他警告:
当模块被重新加载时,它的字典(包含模块的全局变量)被保留。名称的重定义将覆盖旧的定义,因此这通常不是问题。如果模块的新版本未定义由旧版本定义的名称,则旧定义将保留。如果它维护一个全局表或对象缓存,使用try
语句可以测试表的存在并跳过其初始化,如果需要,这个特性可以用于模块的优势:
try:
cache
except NameError:
cache = {}
重新加载内建或动态加载的模块通常不是很有用。不建议重新加载sys
,__main__
,builtins
和其他关键模块。在许多情况下,扩展模块不会被设计为初始化多次,并且可能在重新加载时以任意方式失败。
如果模块使用from … import …从另一个模块导入对象,则对其他模块调用reload()
其中一个方法是重新执行from
语句,另一个方法是使用import
和限定名称(module.name t15>)。
如果一个模块实例化一个类的实例,重新加载定义该类的模块不会影响实例的方法定义 - 它们继续使用旧的类定义。对于派生类同样如此。
版本3.4中的新功能。
详情 http://python.usyiyi.cn/translate/python_352/library/importlib.html#module-importlib
补充一个例子
https://blog.csdn.net/jiaming917/article/details/85792543
Importlib模块与python内置函数__import__详解
**Importlib模块与__import__都可以通过过字符串来导入另外一个模块,但在用法上和本质上都有很大的不同。
应该推荐使用 importlib 的方式
以一个例子为证:
以下为我的工程目录结构**
lib/test.py:
name = "Jack"
def getName():
print(name)
return name
demo.py:
name = "Amy"
def getName():
print(name)
return name
Importlib模块的例子:
testImportlib.py:
import importlib
mName = "lib.test"
module = importlib.import_module(mName)
module.getName()
Importlib是python的一个库,通过导入importlib,调用import_module()方法,传入用户想要获取的模块对应的路径字符串,即可获取一个,模块module,module可以调用这个test模块下的所有属性和方法。
运行截图:
__import__的例子:
import__是python的一个内置方法,直接调用__import()即可获取一个模块.
testImport.py:
mName = "demo"
module = __import__(mName)
module.getName()
此时调用的是相同目录下的demo模块,可顺利地获取这个模块,那么当不再同一个目录下呢?
修改testImport.py:
mName = "lib.test"
module = __import__(mName)
module.getName()
此时的运行结果为:
出错提示为,当前的模块’lib’没有getName()这个方法,因此没有正确获取到test模块。
继续修改此文件:
mName = "lib.test"
module = __import__(mName,fromlist = ('test',))
module.getName()
运行结果为:
当加上fromlist属性即可正确获取。
深入理解
https://www.cnblogs.com/meishandehaizi/p/5863233.html
感觉这篇也太像了,但加了点东西。。https://blog.csdn.net/xc_zhou/article/details/80921546
介绍importlib
Python将
importlib
作为标准库提供。它旨在提供Pythonimport
语法和(__import__()
函数)的实现。另外,importlib提供了开发者可以创建自己的对象(即importer
)来处理导入过程。
Python提供了importlib包作为标准库的一部分。目的就是提供Python中import语句的实现(以及__import__函数)。另外,importlib允许程序员创建他们自定义的对象,可用于引入过程(也称为importer)。
那么
imp
呢?还有一个imp
模块提供了import
语句接口,不过这个模块在Python3.4已经deprecated
了。建议使用importlib来处理。
什么是imp?
另外有一个叫做imp的模块,它提供给Python import语句机制的接口。这个模块在Python 3.4中被否决,目的就是为了只使用importlib。
这个模块比较复杂,文中我们主要探讨如下主题:
- 动态导入
- 检查模块是否可以被导入
- 从源文件导入 ,引入源文件自身
- 第三方模块 import_from_github_com
我们先从动态导入开始。
动态导入
importlib模块支持传递字符串来导入模块。我们先来创建一些简单模块一遍演示。我们在模块里提供了相同接口,通过打印它们自身名字来区分。我们分别创建了foo.py和bar.py,代码如下:
def main():
print(__name__)
现在我们尽需要使用importlib导入它们。我们来看看代码是如何实现的,确保该代码在刚才创建的两个文件的相同目录下。
#importer
import importlib
def dynamic_import(module):
return importlib.import_module(module)
if __name__ == "__main__":
module = dynamic_import('foo')
module.main()
module2 = dynamic_import('bar')
module2.main()
这里我们导入importlib模块,并创建了一个非常简单的函数dynamic_import
。这个函数直接就调用了importlib的import_module方法,并将要导入的模块字符串传递作为参数,最后返回其结果。然后在主入口中我们分别调用了各自的main方法,将打印出各自的name.
$ python3 importer.py
foo
bar
也许你很少会代码这么做,不过在你需要试用字符串作为导入路径的话,那么importlib就有用途了。
模块导入检查
Python有个众所周知的代码风格EAFP: Easier to ask forgiveness than permission.它所代表的意思就是总是先确保事物存在(例如字典中的键)以及在犯错时捕获。如果我们在导入前想检查是否这个模块存在而不是靠猜。 使用mportlib就能实现。
import importlib.util
def check_module(module_name):
"""
Checks if module can be imported without actually
importing it
"""
module_spec = importlib.util.find_spec(module_name)
if module_spec is None:
print("Module: {} not found".format(module_name))
return None
else:
print("Module: {} can be imported".format(module_name))
return module_spec
def import_module_from_spec(module_spec):
"""
Import the module via the passed in module specification
Returns the newly imported module
"""
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
if __name__ == '__main__':
module_spec = check_module('fake_module')
module_spec = check_module('collections')
if module_spec:
module = import_module_from_spec(module_spec)
print(dir(module))
这里我导入了importlib的子模块util
。check_module里面调用find_spec方法, 传递该模块字符串作为参数。当我们分别传入了一个不存在和存在的Python模块。你可以看到当你传入不存在的模块时,find_spec函数将返回 None,在我们代码里就会打印提示。如果存在我们将返回模块的specification。
我们可以通过该模块的specification来实际导入该模块。或者你直接将字符串作为参数调用import_module
函数。不过我这里也学习如何试用模块specification方式导入。看看import_module_from_spec
函数。它接受check_module提供的模块specification作为参数。然后我们将它传递给了module_from_spec
函数,它将返回导入模块。Python文档推荐导入后然后执行模块,所以接下来我们试用exec_module函数执行。最后我们使用dir来确保得到预期模块。
从源代码导入
importlib的子模块有个很好用的技巧我想提提。你可以使用util通过模块的名字和路径来导入模块。
import importlib.util
def import_source(module_name):
module_file_path = module_name.file
module_name = module_name.name
module_spec = importlib.util.spec_from_file_location(
module_name, module_file_path
)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
print(dir((module)))
msg = 'The {module_name} module has the following methods {methods}'
print(msg.format(module_name=module_name, methods=dir(module)))
if name == “main”:
import logging
import_source(logging)
在上面的代码中,我们实际导入logging模块,并将模块传递给了import_source函数。这样我们就可以通过导入的模块获取到实际的路 径和名字。然后我们将信息传递给sec_from_file_location函数,它将返回模块的说明specification。一旦我们获取到模块的说明,我们就可以像前面那样直接通过importlib导入了。
import_from_github_com
这个精巧的包叫做import_from_github_com,它可以用于发现和下载github上的包。为了安装他,你需要做的就是按照如下命令使用pip,
pip install import_from_github_com
1
这个包使用了PEP 302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要Python 3.2或者更高的版本,git和pip才能使用这个包。
一旦这些已经安装,你可以在Python shell中输入如下命令,
>>> from github_com.zzzeek import sqlalchemy
Collecting git+https://github.com/zzzeek/sqlalchemy
Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build
Installing collected packages: SQLAlchemy
Running setup.py install for SQLAlchemy ... done
Successfully installed SQLAlchemy-1.1.0b1.dev0
>>> locals()
{'__builtins__': <module 'builtins' (built-in)>, '__spec__': None,
'__package__': None, '__doc__': None, '__name__': '__main__',
'sqlalchemy': <module 'sqlalchemy' from '/usr/local/lib/python3.5/site-packages/\
sqlalchemy/__init__.py'>,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>}
你如果看了import_from_github_com
的源码,你将会注意到它并没有使用importlib。实际上,它使用了pip来安装那些没有安装的包,然后使用Python的import()函数来引入新安装的模块。这段代码非常值得学习。
总结
目前,你知道如何在代码中使用importlib和import钩子。当然还有很多超出本文所覆盖的知识,这个模块内容非常多,如果你想自定义importer引入器或者loader下载器,那么你可以通过官方文档或者源代码了解更多,但你需要花费很多时间来阅读官方文档和源码。
这里加入一个引入源码的例子:
https://blog.csdn.net/hhczy1003/article/details/76662184
python 动态引入模块 importlib 和 import 不一样
如图所示,应该推荐使用 importlib 的方式。
scrapy 的源码也正是使用的这种方式
附scrapy 源码中 scrapy/utils/misc.py 中引入源码的load_object 实现:
def load_object(path):
"""Load an object given its absolute object path, and return it.
object can be a class, function, variable or an instance.
path ie: 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware'
"""
try:
dot = path.rindex('.')
except ValueError:
raise ValueError("Error loading object '%s': not a full path" % path)
module, name = path[:dot], path[dot+1:]
mod = import_module(module)
try:
obj = getattr(mod, name)
except AttributeError:
raise NameError("Module '%s' doesn't define any object named '%s'" % (module, name))
return obj
在工程中,既然要自动化测试,少不了要导入测试用例。测试用例也使用Python编写,但只有在运行时,才知道所要加载的测试用例的名字。因此,需要在测试工具的框架代码中,加入动态导入某个Python代码的功能。
在Python3.4中,动态导入Module使用的是importlib包的import_module接口(也可以用内建函数__import__,但前一个更好理解)。
用法如下:
import importlib
name = input( 'Please input module name that will be imported:' )
_module = importlib.import_module( name )
_module.some_interface()
在这里,name可以是绝对或相对形式,如pkg.mod或者…mod,可以对应为某个package或者某个路径。例如,如果测试用例first.py在./cases/路径下,则可以输入:cases.first。
稍微介绍一下imp库,尽管现在这个已经用的很少了。
https://blog.csdn.net/qq_41553182/article/details/84798961
当我运行程序的时候,Python Shell给我显示:the imp module is deprecated in favour of importlib
上网搜一下原因,是因为Python3.4版本以上源代码中的引用由imp自动变为importlib.
解决方法只要按照程序报错中指示的路径,如C:\Python36-32\lib\sit-packages\sklearn\externals\joblib\externals\cloudpickle.py
用记事本或者Python编译器打开。
如图,把imp修改为importlib.
然后保存。
如果出现类似 Error 13:pemission denied.是因为这个Python文件对当前电脑登陆用户授予的权限中没有“修改”这一项。
右击“属性”,找到“安全”这一栏,选中User,在下面“修改”这一栏中的“允许”打上勾即可。
然后再回来修改cloudpickle文件的源代码,就不会再出现权限的问题。
https://img-blog.csdn.net/20160831203951063
imp库,python进入import内部
imp模块提供了一个可以实现import语句的接口。
使用imp可以用来导入模块和类。
imp.PY_SOURCE —-1
imp.PY_COMPILED——2
imp.C_EXTENSION——3
imp.get_suffixes()
返回一个列表,列表的元素是三元素元组。
Return a list of 3-element tuples, each describing a particular type of module. Each triple has the form (suffix, mode, type), where suffix is a string to be appended to the module name to form the filename to search for, mode is the mode string to pass to the built-in open() function to open the file (this can be ‘r’ for text files or ‘rb’ for binary files), and type is the file type, which has one of the values PY_SOURCE, PY_COMPILED, or C_EXTENSION, described below.
imp.find_module(name, [,path])
返回值是三元素元组file, pathname, description
file is an open file object positioned at the beginning, pathname is the pathname of the file found, and description is a 3-element tuple as contained in the list returned by get_suffixes() describing the kind of module found.
这里的file可以理解为通过open()打开的一个句柄
例如:a = open(‘/etc/test.sh’)的这个a
imp.load_module(name, file, pathname, description)
例子就是uts中env.py
这个是env.py这个文件里面定义一个类
class A(object)
pass
import os
import sys
import imp
# dir就是env.py所在的目录
dir = os.path.dirname(os.path.abspath())
# 这里有个注意点,可以选择从多个目录中找[dir1, dir2],若果没有找到env会报ImportError
file, path_name, description = imp.find_module('env', [dir])
# 这一步就是导入env这个模块,让B成为A类的别名
B = imp.load_module('env', file,path_name, description).A
当我在其他文件中需要使用B类的时候
from myB import B
好处:我从myB导入了B,但B实际的定义是位于env.py中的A类,所以当我的env.py处于不同位置的时候,我可以导入不同的类B
官方例子,用完之后记得关闭文件
https://blog.csdn.net/foryouslgme/article/details/51734833
Python2与Python3的区别(四):imp与importlib
Deprecated since version 3.4: The imp package is pending deprecation in favor of importlib.
与之相关的:
1、系统函数_import()
2、exec
引用其它人博客
#Python的import不能接受变量,所以应该用 __import__函数来动态导入。
#如下的代码无法正常导入模块
modules = ['OpenSSL', 'Crypto', 'MySQLdb', 'sqlite3', 'zope.interface', 'pyasn1', 'twisted', 'django']
for each in modules:
try:
import each
except Exception, e:
print e
#这样导入会抛出 No module named each 的异常
将 import each 改为 __import__(each)就可以正常导入了。
importlib.find_loader(name, path=None)
Deprecated since version 3.4: Use importlib.util.find_spec() instead.
最后,总结一下:模块导入三种方式及中文注释
https://blog.csdn.net/sjin_1314/article/details/41969227
这里还有一个讲的贼相似的:http://www.cnblogs.com/allenblogs/archive/2011/11/15/2055149.html
Python 有三种模块导入函数
1、 使用import 导入模块
import modname : 模块是指一个可以交互使用,或者从另一Python 程序访问的代码段。只要导入了一个模块,就可以引用它的任何公共的函数、类或属性。模块可以通过这种方法来使用其它模块的功能。
用import语句导入模块,就在当前的名称空间(namespace)建立了一个到该模块的引用.这种引用必须使用全称,也就是说,当使用在被导入模块中定义的函数时,必须包含模块的名字。所以不能只使用 funcname,而应该使用 modname.funcname.
注意:可加入下面的使Python支持中文注释
#coding:gbk或#coding:utf-8或##-*- coding : gbk -*-
导入系统的模块
比如我在家目录下创建一个m.py的文件,我们导入了许多的系统模块,比如time,string等等。这边就类似c++里面的include
#!/bin/env python
import time
import string
import re
import socket
import threading
time.sleep(10)
print "java"
导入私有的模块
m.py文件
#!/bin/env python
#coding:gbk
#coding:utf-8
#上面的加载是支持中文注释
def plus(a ,b):
#"""XXX""" 是函数的说明或使用
"""this is a test +"""
a = a+b
return a+b
使用import 加载m.py模块加载
#!/bin/env python
#coding:gbk
#coding:utf-8
"利用sys模块导入指定Python文件的路径"
import sys
sys.path.append("./")
"导入Python文件"
import m
print m.plus(12,3)
print help(m.plus)
2、 from modname import * 模式加载模块
from modname import funcname
from modname import fa, fb, fc
或者 from modname import *
与第1种方法的区别:funcname 被直接导入到本地名字空间去了,所以它可以直接使用,而不需要加上模块名的限定
- 表示,该模块的所有公共对象(public objects)都被导入到 当前的名称空间,也就是任何只要不是以”_”开始的东西都会被导入。
modname没有被定义,所以modname.funcname这种方式不起作用。并且,如果funcname如果已经被定义,它会被新版本(该导入模块中的版本)所替代。如果funcname被改成指向其他对象,modname不能不会觉察到。
建议:
1)如果你要经常访问模块的属性和方法,且不想一遍又一遍地敲入模块名,使用 from module import
2)如果你想要有选择地导入某些属性和方法,而不想要其它的,使用 from module import
3)如果模块包含的属性和方法与你的某个模块同名,你必须使用import module来避免名字冲突
4)尽量少用 from module import * ,因为判定一个特殊的函数或属性是从哪来的有些困难,并且会造成调试和重构都更困难。
#!/bin/env python
#coding:gbk
#coding:utf-8
"利用sys模块导入指定Python文件的路径"
import sys
sys.path.append("./")
"另外一种导入Python文件"
from m import plus
print plus(12,3)
print help(plus)
3、使用内敛函数__import__
除了前面两种使用import关键字的方法以外,我们还可以使用内建函数 import() 来导入 module。两者的区别是,import 后面跟的必须是一个类型(type),而__import__() 的参数是一个字符串,这个字符串可能来自配置文件,也可能是某个表达式计算结果。例如
mymodule = import (’module_name’)
附注:
1)模块的内容都放在一个模块文件中,如 mymodule 的内容应该放在PYTHONPATH 目录下的一个mymodule.py中,C实现的除外
2)包可以将几个模块名称空间组织起来, 如A.b 就表示在包A中的一个子模块b
#!/bin/env python
#coding:gbk
#coding:utf-8
"利用sys模块导入指定Python文件的路径"
import sys
sys.path.append("./")
"另外一种导入Python文件"
my = __import__("m")
print my.plus(12,3)
print help(my.plus)
参考资料:
1、http://www.cnblogs.com/allenblogs/archive/2011/11/15/2055149.html
2、http://blog.csdn.net/chenguolinblog/article/details/11521611
这里还有一个比较有趣的玩法,介绍修改后实时导入的。。
https://blog.csdn.net/fantasysolo/article/details/82761176
无聊测试了下,发现:
1.python只import模块一次,在import某个模块后就存在内存中,此时修改该模块没有用
2.python3提供 importlib模块,reload方法重新载入模块.
3.但是该reload方法有延迟,似乎并不会等reload完毕,直接会继续执行
4.在reload后加延迟,起到预期效果,两个py互相调用和修改
test2.py:
import test1
import importlib
import time
def modify(ii):
with open('test1.py','r') as f_r:
lines = f_r.readlines()
with open('test1.py','w') as f_w:
i = 1
for line in lines:
if i == 19:
num = int(line)+ii
line = '\t\t'+str(num)+'\n'
i = i+1
f_w.write(line)
def f2():
i2= \
1
print('i2 =',i2)
modify(i2)
importlib.reload(test1)
time.sleep(0.2)
if i2<10000:
test1.f1()
if __name__=='__main__':
f2()
print("test2 end")
test1.py:
import test2
import importlib
import time
def modify(ii):
with open('test2.py','r') as f_r:
lines = f_r.readlines()
with open('test2.py','w') as f_w:
i = 1
for line in lines:
if i == 19:
num = int(line)+ii
line = '\t\t'+str(num)+'\n'
i = i+1
f_w.write(line)
def f1():
i1= \
2
print('i1 =',i1)
modify(i1)
importlib.reload(test2)
time.sleep(0.2)
if i1<10000:
test2.f2()
if __name__=='__main__':
f1()
print("test1 end")