import应该是python代码中比较常见的模块了。import就是导入其他文件中的类,方法,变量,我认为除了主流程逻辑,其他代码文件模块就是为了给别人import的~
对于最通用的import,使用起来确实很方便,但是当遇到问题时也确实头疼,本文就详细介绍下import的使用和我在之前项目中遇到过的问题。
先从代码规范开始,看别人代码你最怕看到哪种import?我最怕遇到随地import...
import我们一般是集中放在文件的开头,PEP8对于导入顺序也有建议:
PEP8 有建议 Python 模块中 import 的导入顺序:
1 引入标准库里的模块
2 引入第三方模块
3 引入自己的模块
4 模块之间用空行间隔
5 属于同一个部分的 import 语句按字母顺序排列
分三大模块这个可以理解,但是我写import还要快速默背一次字母表?不用哈,有第三方库isort帮忙。
安装:pip install isort
使用:isort ***.py
效果:
排序前
isort ***.py
排序后:
导入后,我们打印看下,导入的是什么
<module 'os' from 'C:\\Users\\zoeyxiong\\Anaconda3\\lib\\os.py'>
<module 'sys' (built-in)>
<module 'numpy' from 'C:\\Users\\zoeyxiong\\Anaconda3\\lib\\site-packages\\numpy\\__init__.py'>
其中(built-in)要就是内置模块,os就是标准库。什么是内置模块,什么是标准库呢?
内置模块,什么是内置模块?内置模块还是很好理解的吧,要怎么解释内置模块呢?内置模块就是python中使用C语言编译好的内置在python中的模块。。额,问问chatGPT:
Python中有很多内置模块,例如math、random、os、sys等等。这些模块是Python解释器自带的,无需安装即可使用。
要定义一个内置模块,需要使用C语言编写一个扩展模块,并将其编译成共享库。然后将共享库文件放在Python解释器的
模块搜索路径中,就可以在Python中使用该模块了。
通过上面的打印,os和random不是内置模块啊!chatGPT瞎说!
我质疑了一下,chatGPT知错就改
对不起,我可能没有表达清楚。在Python中,os模块是标准库的一部分,而不是内置函数的一部分。
内置函数,如print()、len()等,是可以直接在Python代码中使用的,无需导入任何模块。
而os模块,虽然是Python解释器自带的,但在使用前需要先导入。所以,当我们说“内置模块”时,
通常是指Python标准库中的模块,这些模块在Python解释器中已经包含,无需额外安装。对于这些模块,
我们只需要使用import语句就可以导入并使用它们。
例如: python import os
print(os.name)
这段代码会打印出你的操作系统的名称。
啊~内置模块和标准库是有区别的。
根据官方文档,标准库包含了内置模块,其中内置模块是内置模块是用C写的,提供了对系统功能的访问比如IO操作。
内置模块有哪些?
print(sys.builtin_module_names)
>>
('_abc', '_ast', '_bisect', '_blake2', '_codecs', '_codecs_cn',
'_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw',
'_collections', '_contextvars', '_csv', '_datetime', '_functools',
'_heapq', '_imp', '_io', '_json', '_locale', '_lsprof', '_md5',
'_multibytecodec', '_opcode', '_operator', '_pickle', '_random',
'_sha1', '_sha256', '_sha3', '_sha512', '_signal', '_sre', '_stat',
'_statistics', '_string', '_struct', '_symtable', '_thread',
'_tracemalloc', '_warnings', '_weakref', '_winapi', '_xxsubinterpreters',
'array', 'atexit', 'audioop', 'binascii', 'builtins', 'cmath', 'errno',
'faulthandler', 'gc', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt',
'nt', 'parser', 'sys', 'time', 'winreg', 'xxsubtype', 'zlib')
了解内置模块和标准库其实主要是想介绍下,import模块的查找顺序
首先导入内置模块,如果导入的不是内置模块,Python 会依次在 sys.path 这个数组中的每个路径中寻找。按照查找优先级,它由三个部分组成:
1. Python 执行的入口文件(比如这里的 main.py)所在的路径
2. 系统的环境变量 $PYTHONPATH 所表示的目录,标准库,也就是C:\\Users\\zoeyxiong\\Anaconda3\\lib这种
3. 第三方库,site 路径,也就是 C:\\Users\\zoeyxiong\\Anaconda3\\lib\\site-packages这种
所以在定义模块的时候,不要和标准库名冲突,除非你想取代它。比如,你自定义了string模块,就用不了标准库string的功能了。
上面介绍了import的规范和导入顺序,那么import 顺序重要吗?
我同意import书写顺序不重要,如果出了问题就是设计缺陷。但是我同样同意PEP8的规范建议,因为你只需要isort一下就有个规范的代码,为什么不做?
举一个设计缺陷+import顺序引起的问题
两个文件,每个模块中都有test_method方法
from test_method1 import test_method
from test_method2 import test_method
result = test_method()
print("result", result)
以上的import操作,test_method1中的test_method就是无效导入。
在import中,还有其他的代码规范建议:
禁止引入无用import
避免使用 import *
命名冲突:如果多个模块中都定义了同名的变量、函数或类,那么使用import *语句可能会导致命名冲突,使代码难以维护。
可读性:使用import *语句会使代码变得难以阅读和理解,因为读者无法立即知道这些变量、函数或类来自哪个模块。
性能:使用import *语句可能会降低代码的性能,因为Python需要在运行时查找模块中的所有变量、函数和类。
因此,建议使用显式的导入语句,例如from module import name或import module,这样可以避免命名冲突,提高代码的可读性和性能。
上面介绍了import在查找模块的顺序,但是还是经常会遇到,明明路径是对的我都能找到文件,还是出现了ModuleNotFoundError的问题。
首先我们先了解模块和包的区别:
模块(module)
python中一个py文件就是一个模块,module_name.py中,module_name就是模块名
包(package)
包就是目录,包里面可以有module,还可以有子包。
在模块导入时,可以是import module,或者from package import module
我们查看下python执行的搜索顺序
import sys
print(sys.path)
['E:\\my_code\\test_import',
'C:\\Users\\zoeyxiong\\Anaconda3\\python38.zip',
'C:\\Users\\zoeyxiong\\Anaconda3\\DLLs',
'C:\\Users\\zoeyxiong\\Anaconda3\\lib',
'C:\\Users\\zoeyxiong\\Anaconda3',
'C:\\Users\\zoeyxiong\\Anaconda3\\lib\\site-packages',
'C:\\Users\\zoeyxiong\\Anaconda3\\lib\\site-packages\\win32',
'C:\\Users\\zoeyxiong\\Anaconda3\\lib\\site-packages\\win32\\lib',
'C:\\Users\\zoeyxiong\\Anaconda3\\lib\\site-packages\\Pythonwin']
现在假设我们的架构如下
--test_import
--package
在test_import目录下执行test_import.py
import sys
print("test_import:\n", sys.path[0])
test_import:
E:\my_code\test_import
在test_import目录下执行test_class.py
python ./package/test_class.py
import sys
print("test_class:\n", sys.path)
test_class:
E:\my_code\test_import\package
在package目录下执行test_class.py
python ./package/test_class.py
test_class:
E:\my_code\test_import\package
在package目录下执行test_import.py
python ../test_import.py
test_import:
E:\my_code\test_import
可以看到,不管在哪个目录下执行脚本,sys_path都是执行脚本所在的路径。
現在我們再增加一個包,结构如下
--test_import
--package
--src
--main.py
在main.py中引用test_class模块,然后执行main.py
from package import test_class
print("this is main.py")
执行 python ./src/main.py
终于复现,常见的问题:
Traceback (most recent call last):
File "./src/main.py", line 11, in <module>
from package import test_class ModuleNotFoundError: No module named 'package'
分析这里,执行文件的目录是E:\my_code\test_import\src
在这个目录直接from package import test_class ,当然找不到package目录
所以我们稍微为相对路径试下
from package import test_class
--》
from ..package import test_class
E:\my_code\test_import\src Traceback (most recent call last): File "src/train.py", line 14, in <module> from ..package import test_class ImportError: attempted relative import with no known parent package
再尝试添加目录
import sys
sys.path.insert(1, r'E:\my_code\test_import')
from package import test_class
print("this is main.py")
执行python ./src/main.py没问题
通过添加sys.path的方式也太粗暴了。。。
如上情况,我们通常做法不是直接执行python ./src/main.py
而是将接口抽取到test_import层去执行
"main.py":
from src import train
from src import infer
if __name__ == '__main__':
print("main")
"infer.py":
from package.sub_package import tool
print("this is infer.py")
"train.py":
from package.sub_package import tool
print("this is train.py")
执行python main.py
this is tool.py
this is train.py
this is infer.py
main
所以,这里是建议
如果是当做脚本文件直接运行的,使用绝对导入
如果是当做模块供其他文件导入,使用相对导入
参考:
https://zhuanlan.zhihu.com/p/498636254
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。