Python凭什么就那么好用呢?毫无疑问,大量现成又好用的内置/第三方库功不可没。
那咱们是怎么应用它们的呢?
噢,对了~是用的import xxx这个语句。
之所以会有此一问,也是之前有一次应用PyCharm进行开发时(又)踩了个坑……
废话少说,先讲问题
像上面这样一个我的项目构造:
Projetc_example|-- A |-- alpha.py |-- beta.py|-- B |-- theta.py|-- main |-- main.py
假如要在main.py中导入theta.py:
# main/main.pyfrom B import theta
显然会导致咱们所不心愿的问题,即Python不晓得要到哪里去找这个名为B的模块(包是一种非凡的模块):
Traceback (most recent call last): File "main/main.py", line 1, in <module> from B import thetaModuleNotFoundError: No module named 'B'
可是这就奇了怪了,为啥同样的代码,在PyCharm里运行就是好的了呢?
import的查找门路
于是咱们不辞艰苦,高低求索,原来在Python中,import语句实际上封装了一系列过程。
- 查找是否已导入同名模块
首先,Python会依照import xxx中指定的包名,到sys.modules中查找以后环境中是否曾经存在相应的包——不要奇怪为什么都没有导入sys这个模块就有sys.modules了。
sys是Python内置模块,也就是亲儿子,导入只是意思一下,让咱们这样的外人在导入的环境中也能够应用相干接口而已,实际上相应的数据对Python而言从始至终都是通明的。
咱们能够导入sys查看一下这个对象的具体内容(节俭篇幅,做省略解决):
>>> import sys>>> sys.modules{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, ...'re': <module 're' from 'E:\\Anaconda\\Anaconda\\lib\\re.py'>, ...}
这些就都是Python一开始就曾经加载好的模块,也就是装置好Python之后,只有一运行环境中就曾经就绪的模块——只是作为外人的咱们还不能间接拿过去用,得跟Python报备一声:“欸,我要拿您儿子来用了嗨~”
很容易能够发现,sys.modules中列出来的已加载模块中存在显著的不同,后面的很多模块显得很洁净,而前面的很多模块都带有from yyy'的字样,并且这个yyy看起来还像是一个门路。
这就关系到咱们接下来要讲的步骤了。
- 在特定门路下查找对应模块
后面咱们讲到了,当咱们导入某个模块时,Python先会去查问sys.modules,看其中是否存在同名模块,查到了那当然大快人心,Python间接把这个模块给咱们用就好了,毕竟儿子那么多,借出去赚点外快也是好事儿不是?
可问题在于:那要是没找到呢?
这显然是一个很事实的问题。毕竟资源是无限的,Python不可能把你可能用到的所有模块全都一股脑给加载起来,否则这样男上加男加男加男……谁也顶不住啊不是(大雾
于是乎就有人给Python出了个主见:那你等到要用的时候,再去找他说他是你儿子呗
Python:妙哇~
图片
有了这个思路,Python就指定了几家特定的酒楼,说:“但凡去生产的各位,都能够给我当儿子。”
就这样,一些原本不是Python亲儿子的人,出于各种起因汇集到了这几家酒楼,以雇佣兵的身份随时筹备长期称为Python的儿子。
这可就比周文王开局就收100个义子优雅多了,养家糊口的压力也就没那么大了(Python:什么?我的亲儿子都不止100个?你说什么?听不见啊——
回到正经的画风来——
实际上,在Python中,sys.path保护的就是这样一个py交易的后果(诶?如同莫名发现了什么),其中保留的内容就是这几家“指定酒楼”,也就是当Python遇到不意识的儿子模块时,就会去实地查找的门路。
咱们也能够打印进去看看具体内容:
>>> sys.path['', 'E:\\Anaconda\\Anaconda\\python37.zip', 'E:\\Anaconda\\Anaconda\\DLLs', 'E:\\Anaconda\\Anaconda\\lib', 'E:\\Anaconda\\Anaconda', 'E:\\Anaconda\\Anaconda\\lib\\site-packages', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin']
大体上就是装置环境时配置的一些包所在门路,其中第一个元素代表以后所执行脚本所在的门路。
也正是因而,咱们能够在同一个目录下,大大方方地调用其余模块。
3. 将模块与名字绑定
找到相应的非亲生模块还没完,加载了包还得为它调配一个指定的名字,咱们能力在脚本中应用这个模块。
当然少数时候咱们感知不到这个过程,因为咱们就是一个import走天下:
import sysimport osimport requests
这个时候咱们指定的模块名,实际上也是指定的稍后用来调用相应模块的对象名称。
换个更显著的:
import requests as req
如果这个时候只应用了第二种形式来导入requests这个模块,那么很显然在之后的程序流程中,咱们都不能应用requests这个名字来调用它而该当应用req。
这就是Python导入过程中的名称绑定,实质上与失常的赋值没有太大区别,加载好了一个对象之后,而后为这个对象赋一个指定的变量名。
当然即便是曾经加载好的模块,咱们也能够利用这个名称绑定的机制为它们取别名,比方:
>>> import sys>>> import sys as sy>>> sys.path['', 'E:\\Anaconda\\Anaconda\\python37.zip', 'E:\\Anaconda\\Anaconda\\DLLs', 'E:\\Anaconda\\Anaconda\\lib', 'E:\\Anaconda\\Anaconda', 'E:\\Anaconda\\Anaconda\\lib\\site-packages', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin']>>> sy.path['', 'E:\\Anaconda\\Anaconda\\python37.zip', 'E:\\Anaconda\\Anaconda\\DLLs', 'E:\\Anaconda\\Anaconda\\lib', 'E:\\Anaconda\\Anaconda', 'E:\\Anaconda\\Anaconda\\lib\\site-packages', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib', 'E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin']>>> sys == syTrue
问题解决
好了,下面就是对Python导入机制的大抵介绍,然而说了半天,咱们的问题还没有解决:在我的项目中如何简洁地跨模块导入其余模块?
在应用PyCharm的时候倒是所有顺遂,因为PyCharm会主动将我的项目的根目录退出到导入的搜寻门路,也就是说像上面这样的我的项目构造,在任意模块中都能够很天然地通过import A导入模块A,用import B导入模块B。
Projetc_example|-- A |-- alpha.py |-- beta.py|-- B |-- theta.py|-- main |-- main.py
然而在非IDE环境中呢?或者说就是原生的Python环境中呢?
很天然地咱们就会想到:那就手动把我的项目根目录退出到sys.path中去嘛。说起来也跟PyCharm做的事没差呀
能够,贫道看你很有悟性,不如跟我去学修仙吧
所以咱们就通过sys和os两个模块七搞八搞(这两个模块以前有过介绍,不再赘述)——
噔噔噔噔——好使了
# Peoject_example/A/alpha.pyprint("name: " + __name__)print("file: " + __file__)def al(): print("Importing alpha succeeded.")main.py中则退出一个逻辑,在sys.path中减少一个我的项目根目录:import osimport syssys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))import A.alphaA.alpha.al()# name: A.alpha# file: *\Project_example\A\alpha.py# Importing alpha succeeded.
功败垂成,风紧扯呼~
总结
本文借由一个易现问题引出对Python导入机制的介绍,实际上限于篇幅,导入机制只是做了一个概览,具体的内容还要更加简单。本文讲到的这三步则实用于比拟常见的情景,理解了这三步也足以应酬很多问题了。
以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈 ,发送 “J” 即可收费获取,每日干货分享