说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 除了 import 语句来导入模块外,还有一个内置的 __import__()
函数,不过这不怎么常用。它还用于动态加载类和函数。
此内置方法可以替代模块 importlib,这个模块是最佳的做法,但你如果坚持用内置的 __import__()
时,可以这么用:
os = __import__('os')
os
# <module 'os' from '/Users/.../lib/python3.10/os.py'>
如果希望名称中包含最右边的子模块,请将非空列表传递给fromlist:
os = __import__('os.path')
path = __import__('os.path', fromlist=[None])
os.path
path # 同上
# <module 'posixpath' from '/Users/.../python3.10/posixpath.py'>
或者,如文档所述,使用 importlib.import_module
模块:
importlib = __import__('importlib')
path = importlib.import_module('os.path')
path
# <module 'posixpath' from '/Users/.../python3.10/posixpath.py'>
语法如下:
__import__(name,
globals=None,
locals=None,
fromlist=(),
level=0
) -> module
参数有:
由于此函数是供Python解释器使用的,而不是一般用途,因此最好使用 importlib.import_module()
以编程方式导入模块。
如果仔细阅读,您会感觉到 API 最初是为了允许从模块延迟加载函数。然而,这不是 CPython 的工作方式,我不知道是否有其他 Python 实现能够做到这一点。
相反,CPython 在第一次导入时执行模块名称空间中的所有代码,然后将模块缓存在 sys.modules
中。__import__()
仍然有用。但是,根据文档了解它的功能相当困难。
此函数会由 import 语句发起调用。 它可以被替换 (通过导入 builtins 模块并赋值给 builtins.__import__)
以便修改 import 语句的语义,但是 强烈 不建议这样做,因为使用导入钩子 (参见 PEP 302) 通常更容易实现同样的目标,并且不会导致代码问题,因为许多代码都会假定所用的是默认实现。 同样也不建议直接使用 import() 而应该用 importlib.import_module()
。
本函数会导入模块 name,利用 globals 和 locals 来决定如何在包的上下文中解释该名称。fromlist 给出了应从 name 模块中导入的对象或子模块的名称。标准的实现代码完全不会用到 locals 参数,只用到了 globals 用于确定 import 语句所在的包上下文。
level 指定是使用绝对还是相对导入。 0 (默认值) 意味着仅执行绝对导入。 level 为正数值表示相对于模块调用 __import__()
的目录,将要搜索的父目录层数 (详情参见 PEP 328)。
当 name 变量的形式为 package.module
时,通常将会返回最高层级的包(第一个点号之前的名称),而 不是 以 name 命名的模块。 但是,当给出了非空的 fromlist 参数时,则将返回以 name 命名的模块。
例如,语句 import spam
的结果将为与以下代码作用相同的字节码:
spam = __import__('spam', globals(), locals(), [], 0)
语句 import spam.ham 的结果将为以下调用:
spam = __import__('spam.ham', globals(), locals(), [], 0)
请注意在这里 import() 是如何返回顶层模块的,因为这是通过 import 语句被绑定到特定名称的对象。
另一方面,语句 from spam.ham import eggs, sausage as saus
的结果将为
_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage
在这里, spam.ham
模块会由 __import__()
返回。 要导入的对象将从此对象中提取并赋值给它们对应的名称。
如果您只想按名称导入模块(可能在包中),请使用 importlib.import_module()
在 3.3 版更改: level 的值不再支持负数(默认值也修改为0)。
在 3.9 版更改: 当使用了命令行参数 -E
或 -I
时,环境变量 PYTHONCASEOK 现在将被忽略。
对 __import__()
封装,使其更加清晰:
def importer(name, root_package=False, relative_globals=None, level=0):
""" We only import modules, functions can be looked up on the module.
Usage:
from foo.bar import baz
>>> baz = importer('foo.bar.baz')
import foo.bar.baz
>>> foo = importer('foo.bar.baz', root_package=True)
>>> foo.bar.baz
from .. import baz (level = number of dots)
>>> baz = importer('baz', relative_globals=globals(), level=2)
"""
return __import__(name, locals=None, # locals has no use
globals=relative_globals,
fromlist=[] if root_package else [None],
level=level)
使用:
baz = importer('foo.bar.baz')
foo = importer('foo.bar.baz', root_package=True)
baz2 = importer('bar.baz', relative_globals=globals(), level=2)
assert foo.bar.baz is baz is baz2
模块中名称的动态访问,要从 baz 模块中按名称动态访问全局变量,请使用 getattr:
for name in dir(baz):
print(getattr(baz, name))
您可以使用 __import__()
更改或拦截导入行为。在这种情况下,让我们只打印它得到的参数,以证明我们正在拦截它:
old_import = __import__
def noisy_importer(name, locals, globals, fromlist, level):
print(f'name: {name!r}')
print(f'fromlist: {fromlist}')
print(f'level: {level}')
return old_import(name, locals, globals, fromlist, level)
import builtins
builtins.__import__ = noisy_importer
现在,当您导入时,您可以看到这些重要的参数。
>>> from os.path import join as opj
name: 'os.path'
fromlist: ('join',)
level: 0
>>> opj
<function join at 0x7fd08d882618>
也许在这种情况下,获取全局或局部数据可能会很有用,但并没有立即想到它的具体用途。
更新时间:May 23, 2022, 10:27 p.m. 标签:python 模块 import