关于Python引入外部模块的import语句的讨论
在前面文章代码中,常常出现了from xx import *
,有读者指出这种导入模块的方式存在一定的风险,因此我针对这个问题进行了一定的扩展研究。
引入模块的常用语句
在写代码过程中,我们常常需要用到一些外部库(模块)的方法,而在使用这些外部库之前,需要先把外部库引入当前的项目空间中,常用的引入外部模块的方法有以下3种,这里以引入 pandas
库为例:
(1)使用 import 语句导入模块
import pandas
import pandas as pd
(2)使用 from … import … 导入模块方法
from pandas import DataFrame
from pandas import DataFrame as df_fun
from pandas import * # 暴露模块的所有变量及方法
(3)使用 import 导入模块
这个方法不常用,如下代码等价于 import pandas as pd
。
pd = __import__("pandas")
引入模块的实际过程
先来看当我们用 import
引入模块时,Python实际发生了什么过程。
(1)Python 预加载模块
在Python初始化运行环境的时候,会预先加载一批内建模块到内存中,这些预加载的模块的信息通过字典的形式存储在 sys.modules
,可以通过以下代码查看(仅展示部分):
import sys
print("预加载模块:", )
for modules_name, info_ in sys.modules.items():
print(modules_name, info_)
加载模块:
sys <module 'sys' (built-in)>
builtins <module 'builtins' (built-in)>
_frozen_importlib <module '_frozen_importlib' (frozen)>
_imp <module '_imp' (built-in)>
_thread <module '_thread' (built-in)>
_warnings <module '_warnings' (built-in)>
encodings <module 'encodings' from 'F:\\Anaconda\\Lib\\encodings\\__init__.py'>
......
(2)引入外部库的信息
当我们要 import
一个模块时,会先在 sys.modules
当中搜索该模块名,若搜索得到,则将该模块名添加到当前代码的局部命名空间。例如,我们引入加载模块中的 encodings
模块:
print(dir())
import encodings
print(dir())
可以看到,在局部命名空间中,引入了 encodings
模块,此时可以在调用该模块的属性及方法。
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'encodings']
反之,如果我们引入的模块不在 sys.modules
当中,则会去指定的存放外部模块信息的文件夹下搜索是否存在该模块,若存在,则引入该模块的相关信息。例如引入 pandas
模块,并打印模块信息:
print(sys.modules['pandas'] if 'pandas' in sys.modules.keys() else "No Exist")
import pandas
print(sys.modules['pandas'] if 'pandas' in sys.modules.keys() else "No Exist")
No Exist
<module 'pandas' from 'F:\\Anaconda\\Lib\\site-packages\\pandas\\__init__.py'>
值得注意的是,这里pandas
模块信息显示该模块的文件夹地址,也就是说引入模块,引入的是模块的地址,调用的时候还是原来文件夹下的模块。
综上可知,当引入一个模块时,该模块对应的字符串,会被引入局部命名空间,以及 sys.modules
当中。例如,import pandas
,则字符串 'pandas'
会被引入局部命名空间和sys.modules
中;如果是 import pandas as pd
,则字符串'pd'
会被引入局部命名空间和sys.modules
中。
不同引入方式的区别
基于上部分对 import
的介绍,我们来看下 from xx import xxx
的引入方式有什么不一样。
print(sys.modules['pandas'] if 'pandas' in sys.modules.keys() else "No Exist")
print(dir())
from pandas import DataFrame
print(sys.modules['pandas'] if 'pandas' in sys.modules.keys() else "No Exist")
print(dir())
No Exist
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
<module 'pandas' from 'F:\\Anaconda\\Lib\\site-packages\\pandas\\__init__.py'>
['DataFrame', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
发现,在 sys.modules
中创建的模块名还是 pandas
,但在命名空间中新增的字符串是 DataFrame
,此时 pandas
库下的 DataFrame
方法直接暴露在命名空间中,可以直接进行调用。
df = DataFrame()
print(type(df))
<class 'pandas.core.frame.DataFrame'>
进一步地,就会知道,当我们用 from pandas import *
,就会将该模块的全部变量及方法暴露到当前的命名空间当中。
由此可见:不同的引入模块方式存在一定的区别,而这些区别,往往会在一些场景下引发问题,例如,使用 from xx import xxx
的方法,可能在引入具有相同方法名的不同模块时,造成命名空间的冲突。.
建议性结论
综上所述,得到几点关于在使用 import
引入外部模块时要注意的建议性结论:
- 为了防止命名空间的冲突,尽量使用
import xx
的形式,或者用import xx as x
的形式。需要调用模块方法时用模块名.方法名()
;除非有些模块名.方法名()
过于冗长繁琐,可以少量地使用该语句; - 尽量避免使用
from a import *
,该语句将模块的全部属性和方法导入到命名空间,极易造成命名冲突,也就是部分引入的信息被后来的引入信息所覆盖。 - 多尝试重命名
as
引入的模块(子模块、方法),不仅可以使引用更加方便,且可以人为地避免命名冲突。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!