前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python--如何优雅的import

python--如何优雅的import

原创
作者头像
languageX
修改2023-08-08 10:47:07
7740
修改2023-08-08 10:47:07
举报
文章被收录于专栏:计算机视觉CV计算机视觉CV

import应该是python代码中比较常见的模块了。import就是导入其他文件中的类,方法,变量,我认为除了主流程逻辑,其他代码文件模块就是为了给别人import的~

对于最通用的import,使用起来确实很方便,但是当遇到问题时也确实头疼,本文就详细介绍下import的使用和我在之前项目中遇到过的问题。

import代码规范

先从代码规范开始,看别人代码你最怕看到哪种import?我最怕遇到随地import...

import我们一般是集中放在文件的开头,PEP8对于导入顺序也有建议:

PEP8 有建议 Python 模块中 import 的导入顺序:

代码语言:javascript
复制
1 引入标准库里的模块 
2 引入第三方模块 
3 引入自己的模块 
4 模块之间用空行间隔
5 属于同一个部分的 import 语句按字母顺序排列

分三大模块这个可以理解,但是我写import还要快速默背一次字母表?不用哈,有第三方库isort帮忙。

安装:pip install isort

使用:isort ***.py

效果:

排序前

isort ***.py

排序后:

import内置模块和标准库

导入后,我们打印看下,导入的是什么

代码语言:javascript
复制
<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
复制
Python中有很多内置模块,例如math、random、os、sys等等。这些模块是Python解释器自带的,无需安装即可使用。
要定义一个内置模块,需要使用C语言编写一个扩展模块,并将其编译成共享库。然后将共享库文件放在Python解释器的
模块搜索路径中,就可以在Python中使用该模块了。

通过上面的打印,os和random不是内置模块啊!chatGPT瞎说!

我质疑了一下,chatGPT知错就改

代码语言:javascript
复制
对不起,我可能没有表达清楚。在Python中,os模块是标准库的一部分,而不是内置函数的一部分。
内置函数,如print()、len()等,是可以直接在Python代码中使用的,无需导入任何模块。
而os模块,虽然是Python解释器自带的,但在使用前需要先导入。所以,当我们说“内置模块”时,
通常是指Python标准库中的模块,这些模块在Python解释器中已经包含,无需额外安装。对于这些模块,
我们只需要使用import语句就可以导入并使用它们。
例如:  python  import os
print(os.name) 
这段代码会打印出你的操作系统的名称。

啊~内置模块和标准库是有区别的。

根据官方文档,标准库包含了内置模块,其中内置模块是内置模块是用C写的,提供了对系统功能的访问比如IO操作。

内置模块有哪些?

代码语言:javascript
复制
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 的查找顺序

了解内置模块和标准库其实主要是想介绍下,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方法

代码语言:javascript
复制
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 *

代码语言:javascript
复制
命名冲突:如果多个模块中都定义了同名的变量、函数或类,那么使用import *语句可能会导致命名冲突,使代码难以维护。  
可读性:使用import *语句会使代码变得难以阅读和理解,因为读者无法立即知道这些变量、函数或类来自哪个模块。  
性能:使用import *语句可能会降低代码的性能,因为Python需要在运行时查找模块中的所有变量、函数和类。  
因此,建议使用显式的导入语句,例如from module import name或import module,这样可以避免命名冲突,提高代码的可读性和性能。

import中的ModuleNotFoundError问题

上面介绍了import在查找模块的顺序,但是还是经常会遇到,明明路径是对的我都能找到文件,还是出现了ModuleNotFoundError的问题。

首先我们先了解模块和包的区别:

模块(module)

python中一个py文件就是一个模块,module_name.py中,module_name就是模块名

包(package)

包就是目录,包里面可以有module,还可以有子包。

在模块导入时,可以是import module,或者from package import module

我们查看下python执行的搜索顺序

代码语言:javascript
复制
import sys
print(sys.path)
代码语言:javascript
复制
['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_class.py

test_import.py

在test_import目录下执行test_import.py

代码语言:javascript
复制
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

代码语言:javascript
复制
import sys 
print("test_class:\n", sys.path)
test_class: 
E:\my_code\test_import\package

在package目录下执行test_class.py

代码语言:javascript
复制
python  ./package/test_class.py
test_class: 
E:\my_code\test_import\package

在package目录下执行test_import.py

代码语言:javascript
复制
python ../test_import.py 
test_import:
E:\my_code\test_import

可以看到,不管在哪个目录下执行脚本,sys_path都是执行脚本所在的路径。

現在我們再增加一個包,结构如下

--test_import

--package

--test_class.py

--src

--main.py

test_import.py

在main.py中引用test_class模块,然后执行main.py

代码语言:javascript
复制
from package import test_class 
print("this is main.py")

执行 python ./src/main.py

终于复现,常见的问题:

代码语言:javascript
复制
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

代码语言:javascript
复制
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 

再尝试添加目录

代码语言:javascript
复制
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层去执行

代码语言:javascript
复制
"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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • import代码规范
  • import内置模块和标准库
  • import 的查找顺序
  • import中的ModuleNotFoundError问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档