Python 一般存在以下三种方式:
最后介绍一下在动态加载时经常用到的 inspect 模块。
__import__ (name[, globals[, locals[, fromlist[, level]]]])
参数说明:
# test.py
def test():
print("test")
class Test(object):
def __init__(self):
print("Test Create")
class SubTest(Test):
def __init__(self):
print("Test2 Create")
动态加载 test.py
c = __import__('test')
print(c)
c.test()
obj = c.Test()
obj_sub = c.SubTest()
'''
test
Test Create
Test2 Create
'''
如果输入的参数如果带有 “.”,采用 __import__ 直接导入 module 容易造成意想不到的结果。 OpenStack 的 oslo.utils 封装了 __import__,支持动态导入 class, object 等。
# Copyright 2011 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Import related utilities and helper functions.
"""
import sys
import traceback
def import_class(import_str):
"""Returns a class from a string including module and class.
.. versionadded:: 0.3
"""
mod_str, _sep, class_str = import_str.rpartition('.')
__import__(mod_str)
try:
return getattr(sys.modules[mod_str], class_str)
except AttributeError:
raise ImportError('Class %s cannot be found (%s)' %
(class_str,
traceback.format_exception(*sys.exc_info())))
def import_object(import_str, *args, **kwargs):
"""Import a class and return an instance of it.
.. versionadded:: 0.3
"""
return import_class(import_str)(*args, **kwargs)
def import_object_ns(name_space, import_str, *args, **kwargs):
"""Tries to import object from default namespace.
Imports a class and return an instance of it, first by trying
to find the class in a default namespace, then failing back to
a full path if not found in the default namespace.
.. versionadded:: 0.3
.. versionchanged:: 2.6
Don't capture :exc:`ImportError` when instanciating the object, only
when importing the object class.
"""
import_value = "%s.%s" % (name_space, import_str)
try:
cls = import_class(import_value)
except ImportError:
cls = import_class(import_str)
return cls(*args, **kwargs)
def import_module(import_str):
"""Import a module.
.. versionadded:: 0.3
"""
__import__(import_str)
return sys.modules[import_str]
def import_versioned_module(module, version, submodule=None):
"""Import a versioned module in format {module}.v{version][.{submodule}].
:param module: the module name.
:param version: the version number.
:param submodule: the submodule name.
:raises ValueError: For any invalid input.
.. versionadded:: 0.3
.. versionchanged:: 3.17
Added *module* parameter.
"""
# NOTE(gcb) Disallow parameter version include character '.'
if '.' in '%s' % version:
raise ValueError("Parameter version shouldn't include character '.'.")
module_str = '%s.v%s' % (module, version)
if submodule:
module_str = '.'.join((module_str, submodule))
return import_module(module_str)
def try_import(import_str, default=None):
"""Try to import a module and if it fails return default."""
try:
return import_module(import_str)
except ImportError:
return default
def import_any(module, *modules):
"""Try to import a module from a list of modules.
:param modules: A list of modules to try and import
:returns: The first module found that can be imported
:raises ImportError: If no modules can be imported from list
.. versionadded:: 3.8
"""
for module_name in (module,) + modules:
imported_module = try_import(module_name)
if imported_module:
return imported_module
raise ImportError('Unable to import any modules from the list %s' %
str(modules))
动态加载某个模块
import importlib
itertools = importlib.import_module('itertools')
importlib.import_module(name, package=None) 参数说明:
import_module() 函数是一个对 importlib.__import__() 进行简化的包装器。 这意味着该函数的所有主义都来自于 importlib.__import__()。 这两个函数之间最重要的不同点在于 import_module() 返回指定的包或模块 (例如 pkg.mod),而 __import__() 返回最高层级的包或模块 (例如 pkg)。
如果动态导入一个自从解释器开始执行以来被创建的模块(即创建了一个 Python 源代码文件),为了让导入系统知道这个新模块,可能需要调用 invalidate_caches()。
判断某个模块是否可以被加载
import importlib.util
import sys
# For illustrative purposes.
name = 'itertools'
spec = importlib.util.find_spec(name)
if spec is None:
print("can't find the itertools module")
else:
# If you chose to perform the actual import ...
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Adding the module to sys.modules is optional.
sys.modules[name] = module
其中 importlib.util.find_spec(name, package=None) 参数与 importlib.import_module(name, package=None) 意义相同,其返回一个命名空间,其中包含用于加载模块的相关导入信息。importlib.util.module_from_spec(spec) 从 spec 中创建一个新的模块,之后就可以是使用 module 当 itertools 使用。spec.loader.exec_module(module) 执行某个模块。
直接从文件中加载
import importlib.util
import sys
# For illustrative purposes.
import tokenize
file_path = tokenize.__file__
module_name = tokenize.__name__
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Optional; only necessary if you want to be able to import the module
# by name later.
sys.modules[module_name] = module
importlib.util.spec_from_file_location(name, location, *, loader=None, submodule_search_locations=None) 其根据指向某个文件的路径创建一个 ModuleSpec 实例。例子中 module 可以直接当成 tokenize 使用。
通过 importer 来管理 finder 和 loader
import importlib.machinery
import sys
# For illustrative purposes only.
SpamMetaPathFinder = importlib.machinery.PathFinder
SpamPathEntryFinder = importlib.machinery.FileFinder
loader_details = (importlib.machinery.SourceFileLoader,
importlib.machinery.SOURCE_SUFFIXES)
# Setting up a meta path finder.
# Make sure to put the finder in the proper location in the list in terms of
# priority.
sys.meta_path.append(SpamMetaPathFinder)
# Setting up a path entry finder.
# Make sure to put the path hook in the proper location in the list in terms
# of priority.
sys.path_hooks.append(SpamPathEntryFinder.path_hook(loader_details))
实现 importlib.import_module 函数
import importlib.util
import sys
def import_module(name, package=None):
"""An approximate implementation of import."""
absolute_name = importlib.util.resolve_name(name, package)
try:
return sys.modules[absolute_name]
except KeyError:
pass
path = None
if '.' in absolute_name:
parent_name, _, child_name = absolute_name.rpartition('.')
parent_module = import_module(parent_name)
path = parent_module.__spec__.submodule_search_locations
for finder in sys.meta_path:
spec = finder.find_spec(absolute_name, path)
if spec is not None:
break
else:
msg = f'No module named {absolute_name!r}'
raise ModuleNotFoundError(msg, name=absolute_name)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[absolute_name] = module
if path is not None:
setattr(parent_module, child_name, module)
return module
eval(expression, globals=None, locals=None)
exec(object[, globals[, locals]])
参数说明:
exec 函数与 eval 函数有很多类似之处,主要的不同有以下两点:
x = 10
def func():
y = 20
a = eval('x + y')
print('a: ', a)
b = eval('x + y', {'x': 1, 'y': 2})
print('b: ', b)
c = eval('x + y', {'x': 1, 'y': 2}, {'y': 3, 'z': 4})
print('c: ', c)
d = eval('print(x, y)')
print('d: ', d)
func()
'''
a: 30
b: 3
c: 4
10 20
d: None
'''
x = 10
expr = """
z = 30
sum = x + y + z
print(sum)
"""
def func():
y = 20
exec(expr)
exec(expr, {'x': 1, 'y': 2})
exec(expr, {'x': 1, 'y': 2}, {'y': 3, 'z': 4})
func()
'''
60
33
34
'''
inspect.getmembers(object[, predicate])
其实现了提取某个对象 object 中的所有成员,以(name,value)对组成的列表返回。其中第二个参数通常可以根据需要调用如下16个方法:
inspect.ismodule(object): 是否为模块
inspect.isclass(object):是否为类
inspect.ismethod(object):是否为方法(bound method written in python)
inspect.isfunction(object):是否为函数(python function, including lambda expression)
inspect.isgeneratorfunction(object):是否为python生成器函数
inspect.isgenerator(object):是否为生成器
inspect.istraceback(object): 是否为traceback
inspect.isframe(object):是否为frame
inspect.iscode(object):是否为code
inspect.isbuiltin(object):是否为built-in函数或built-in方法
inspect.isroutine(object):是否为用户自定义或者built-in函数或方法
inspect.isabstract(object):是否为抽象基类
inspect.ismethoddescriptor(object):是否为方法标识符
inspect.isdatadescriptor(object):是否为数字标识符,数字标识符有__get__ 和__set__属性; 通常也有__name__和__doc__属性
inspect.isgetsetdescriptor(object):是否为getset descriptor
inspect.ismemberdescriptor(object):是否为member descriptor